[libc-commits] [compiler-rt] [lldb] [flang] [lld] [clang-tools-extra] [libc] [libcxxabi] [llvm] [clang] [mlir] [libunwind] [libcxx] [libc++][numeric] P0543R3: Saturation arithmetic (PR #77967)
Hristo Hristov via libc-commits
libc-commits at lists.llvm.org
Wed Jan 17 02:41:22 PST 2024
https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/77967
>From 48c4463e8817c8ee0f00ffa7422e6fafbe838275 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Wed, 10 Jan 2024 13:46:19 +0200
Subject: [PATCH 01/20] [libc++][numeric] P0543R3: Saturation arithmetic
Implements: https://wg21.link/P0543R3
- https://eel.is/c++draft/numeric.sat
Additional notes:
- Division: https://eel.is/c++draft/expr.mul#4
- Arithmetic conversions: https://eel.is/c++draft/expr.arith.conv#1.5
- Clang builtins: https://clang.llvm.org/docs/LanguageExtensions.html#builtin-functions
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/docs/ReleaseNotes/18.rst | 7 +-
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
libcxx/include/CMakeLists.txt | 1 +
.../include/__numeric/saturation_arithmetic.h | 135 ++++++++++++++++++
libcxx/include/module.modulemap.in | 1 +
libcxx/include/numeric | 13 ++
libcxx/include/version | 2 +-
libcxx/modules/std/numeric.inc | 10 ++
.../numeric.version.compile.pass.cpp | 16 +--
.../version.version.compile.pass.cpp | 16 +--
.../numeric.ops.sat/add_sat.pass.cpp | 129 +++++++++++++++++
.../numeric.ops.sat/add_sat.verify.cpp | 39 +++++
.../numeric.ops.sat/div_sat.assert.pass.cpp | 53 +++++++
.../numeric.ops.sat/div_sat.pass.cpp | 108 ++++++++++++++
.../numeric.ops.sat/div_sat.verify.cpp | 39 +++++
.../numeric.ops.sat/mul_sat.pass.cpp | 119 +++++++++++++++
.../numeric.ops.sat/mul_sat.verify.cpp | 39 +++++
.../numeric.ops.sat/saturate_cast.pass.cpp | 132 +++++++++++++++++
.../numeric.ops.sat/saturate_cast.verify.cpp | 44 ++++++
.../numeric.ops.sat/sub_sat.pass.cpp | 107 ++++++++++++++
.../numeric.ops.sat/sub_sat.verify.cpp | 39 +++++
.../generate_feature_test_macro_components.py | 5 +-
23 files changed, 1026 insertions(+), 32 deletions(-)
create mode 100644 libcxx/include/__numeric/saturation_arithmetic.h
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.assert.pass.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
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..61ae35f3ee02d5
--- /dev/null
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -0,0 +1,135 @@
+// -*- 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<_Tp> || __libcpp_signed_integer<_Tp>;
+// concept __libcpp_standard_integer =
+// __libcpp_unsigned_integer<remove_cv<_Tp>> || __libcpp_signed_integer<remove_cv<_Tp>>;
+concept __libcpp_standard_integer = __libcpp_unsigned_integer<decay_t<_Tp>> || __libcpp_signed_integer<decay_t<_Tp>>;
+
+template <__libcpp_standard_integer _Tp>
+// requires __libcpp_standard_integer<_Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
+ // builtins: clang/docs/LanguageExtensions.rst
+ // builtins:
+ // https://github.com/llvm/llvm-project/blob/7b45c549670a8e8b6fe90f4382b0699dd20707d3/clang/docs/LanguageExtensions.rst#L3500
+ 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 && __y > 0)
+ // // Overflows if (x > 0 && y > 0)
+ // return std::numeric_limits<_Tp>::max();
+ // else if (__y > 0)
+ // // Overflows if (x > 0 && y < 0)
+ // return std::numeric_limits<_Tp>::max();
+ 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 {
+ // if (std::in_range<_Rp>(__x)) {
+ // return _Rp{__x};
+ // }
+ // 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..0800319de820d9 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::sub_sat;
+ using std::mul_sat;
+ using std::div_sat;
+ using std::saturate_cast;
+#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..2964ce11d8b654
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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;
+}
+
+// ADDITIONAL_COMPILE_FLAGS: -Wno-constant-conversion
+
+constexpr void cppreference_test() {
+ {
+ constexpr int a = std::add_sat(3, 4); // no saturation occurs, T = int
+ static_assert(a == 7);
+
+ constexpr unsigned char b = std::add_sat<unsigned char>(UCHAR_MAX, 4); // saturated
+ static_assert(b == UCHAR_MAX);
+
+ constexpr unsigned char c = std::add_sat(UCHAR_MAX, 4); // not saturated, T = int
+ // add_sat(int, int) returns int tmp == 259,
+ // then assignment truncates 259 % 256 == 3
+ static_assert(c == 3);
+
+ // unsigned char d = std::add_sat(252, c); // Error: inconsistent deductions for T
+
+ constexpr unsigned char e = std::add_sat<unsigned char>(251, a); // saturated
+ static_assert(e == UCHAR_MAX);
+ // 251 is of type T = unsigned char, `a` is converted to unsigned char value;
+ // might yield an int -> unsigned char conversion warning for `a`
+
+ constexpr signed char f = std::add_sat<signed char>(-123, -3); // not saturated
+ static_assert(f == -126);
+
+ constexpr signed char g = std::add_sat<signed char>(-123, -13); // saturated
+ static_assert(g == std::numeric_limits<signed char>::min()); // g == -128
+ }
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ cppreference_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..0411002d7c199e
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 div_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) div = std::div_sat(IntegerT{3}, IntegerT{4});
+ assert(div == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) div = std::div_sat(maxVal, minVal);
+ assert(div == (maxVal / minVal));
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, maxVal);
+ assert(div == (minVal / maxVal));
+ }
+
+ // Saturation - max only
+ {
+ std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, IntegerT{-1});
+ assert(div == maxVal);
+ }
+
+ 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) div = std::div_sat(IntegerT{3}, IntegerT{4});
+ assert(div == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, maxVal);
+ assert(div == (minVal / maxVal));
+ }
+
+ // Unsigned integer devision never overflow
+
+ 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;
+}
+
+constexpr void cppreference_test() {
+ {
+ static_assert("" && (std::div_sat<int>(6, 3) == 2) // not saturated
+ && (std::div_sat<int>(INT_MIN, -1) == INT_MAX) // saturated
+ && (std::div_sat<unsigned>(6, 3) == 2) // not saturated
+ );
+ }
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ cppreference_test();
+
+ return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
new file mode 100644
index 00000000000000..e8ab8ef0c148f4
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_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 div_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 'div_sat'}}
+ // expected-error@*:* 0-2 {{expected unqualified-id}}
+ [[maybe_unused]] auto sum = std::div_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/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
new file mode 100644
index 00000000000000..1192b6b4a61dc0
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mul_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::mul_sat(minVal, maxVal)));
+
+ // No saturation
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(IntegerT{3}, IntegerT{4});
+ assert(sum == IntegerT{12});
+ }
+
+ // Saturation - max - both arguments positive
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{4});
+ assert(sum == maxVal);
+ }
+
+ // Saturation - max - both arguments negative
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, IntegerT{-4});
+ assert(sum == maxVal);
+ }
+
+ // Saturation - min - left positive, right negative
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{-4});
+ assert(sum == minVal);
+ }
+
+ // Saturation - min - left negative, right positive
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::mul_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::mul_sat(minVal, maxVal)));
+
+ // No saturation
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(IntegerT{3}, IntegerT{4});
+ assert(sum == IntegerT{12});
+ }
+
+ // Saturation
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::mul_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;
+}
+
+constexpr void cppreference_test() {
+ {
+ static_assert(
+ "" && (std::mul_sat<int>(2, 3) == 6) // not saturated
+ && (std::mul_sat<int>(INT_MAX / 2, 3) == INT_MAX) // saturated
+ && (std::mul_sat<int>(-2, 3) == -6) // not saturated
+ && (std::mul_sat<int>(INT_MIN / -2, -3) == INT_MIN) // saturated
+ && (std::mul_sat<unsigned>(2, 3) == 6) // not saturated
+ && (std::mul_sat<unsigned>(UINT_MAX / 2, 3) == UINT_MAX) // saturated
+ );
+ }
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ cppreference_test();
+
+ return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
new file mode 100644
index 00000000000000..8113be2fd23e6b
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_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 mul_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 'mul_sat'}}
+ // expected-error@*:* 0-2 {{expected unqualified-id}}
+ [[maybe_unused]] auto sum = std::mul_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/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
new file mode 100644
index 00000000000000..889b1864f128e5
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -0,0 +1,132 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 R, class T>
+// constexpr R saturate_cast(T x) noexcept; // freestanding
+
+#include <cassert>
+#include <concepts>
+#include <limits>
+#include <numeric>
+
+#include <print>
+
+template <typename IntegerResultT, typename IntegerT>
+constexpr bool test_signed_notsaturated() {
+ constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+ constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+ static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
+ static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+
+ assert(std::saturate_cast<IntegerResultT>(minVal) == minVal);
+ assert(std::saturate_cast<IntegerResultT>(maxVal) == maxVal);
+
+ return true;
+}
+
+template <typename IntegerResultT, typename IntegerT>
+constexpr bool test_signed_saturated() {
+ constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+ constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+ static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
+ static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+
+ assert(std::saturate_cast<IntegerResultT>(minVal) == std::numeric_limits<IntegerResultT>::min());
+ assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
+
+ return true;
+}
+
+template <typename IntegerResultT, typename IntegerT>
+constexpr bool test_unsigned_notsaturated() {
+ constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+ constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+ static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
+ static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+
+ assert(std::saturate_cast<IntegerResultT>(minVal) == minVal);
+ assert(std::saturate_cast<IntegerResultT>(maxVal) == maxVal);
+
+ return true;
+}
+
+template <typename IntegerResultT, typename IntegerT>
+constexpr bool test_unsigned_saturated() {
+ constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+ constexpr auto maxVal = std::numeric_limits<IntegerT>::max();
+
+ static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
+ static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+
+ assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
+ assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
+
+ return true;
+}
+
+constexpr bool test() {
+ // signed
+ test_signed_notsaturated<long long int, signed char>();
+ test_signed_notsaturated<long long int, short int>();
+ test_signed_notsaturated<long long int, int>();
+ test_signed_notsaturated<long long int, long int>();
+ test_signed_notsaturated<long long int, long long int>();
+ test_signed_saturated<signed char, long long int>();
+ test_signed_saturated<short int, long long int>();
+ test_signed_saturated<int, long long int>();
+ test_signed_saturated<long int, long long int>();
+ test_signed_saturated<long long int, long long int>();
+ // unsigned
+ test_unsigned_notsaturated<unsigned long long int, unsigned char>();
+ test_unsigned_notsaturated<unsigned long long int, unsigned short int>();
+ test_unsigned_notsaturated<unsigned long long int, unsigned int>();
+ test_unsigned_notsaturated<unsigned long long int, unsigned long int>();
+ test_unsigned_notsaturated<unsigned long long int, unsigned long long int>();
+ test_unsigned_saturated<unsigned char, unsigned long long int>();
+ test_unsigned_saturated<unsigned short int, unsigned long long int>();
+ test_unsigned_saturated<unsigned int, unsigned long long int>();
+ test_unsigned_saturated<unsigned long int, unsigned long long int>();
+ test_unsigned_saturated<unsigned long long int, unsigned long long int>();
+
+ return true;
+}
+
+constexpr void cppreference_test() {
+ {
+ constexpr std::int16_t x1{696};
+
+ constexpr std::int8_t x2 = std::saturate_cast<std::int8_t>(x1);
+ static_assert(x2 == std::numeric_limits<std::int8_t>::max());
+
+ constexpr std::uint8_t x3 = std::saturate_cast<std::uint8_t>(x1);
+ static_assert(x3 == std::numeric_limits<std::uint8_t>::max());
+
+ constexpr std::int16_t y1{-696};
+
+ constexpr std::int8_t y2 = std::saturate_cast<std::int8_t>(y1);
+ static_assert(y2 == std::numeric_limits<std::int8_t>::min());
+
+ constexpr std::uint8_t y3 = std::saturate_cast<std::uint8_t>(y1);
+ static_assert(y3 == 0);
+ }
+}
+
+int main(int, char**) {
+ test();
+ // static_assert(test());
+ cppreference_test();
+
+ return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
new file mode 100644
index 00000000000000..d8895225e7468e
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 R, class T>
+// constexpr R saturate_cast(T x) noexcept; // freestanding
+
+#include <cstdint>
+#include <numeric>
+
+#include "test_macros.h"
+
+template <typename ResultIntegerT, typename IntegerT>
+constexpr void test_constraint() {
+ // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
+ // expected-error@*:* 0-6 {{no matching function for call to 'saturate_cast'}}
+ // expected-error@*:* 0-4 {{expected unqualified-id}}
+ [[maybe_unused]] auto sum = std::saturate_cast<ResultIntegerT>(IntegerT{4});
+}
+
+constexpr bool test() {
+ test_constraint<bool, int>();
+ test_constraint<char, int>();
+ test_constraint<int, bool>();
+ test_constraint<int, char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_constraint<wchar_t, int>();
+ test_constraint<int, wchar_t>();
+#endif
+ test_constraint<std::char16_t, int>();
+ test_constraint<std::char32_t, int>();
+ test_constraint<int, std::char16_t>();
+ test_constraint<int, std::char32_t>();
+
+ return true;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
new file mode 100644
index 00000000000000..a3f0d175ec350d
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 sub_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::sub_sat(minVal, maxVal)));
+
+ // No saturation
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(IntegerT{3}, IntegerT{4});
+ assert(sum == IntegerT{-1});
+ }
+
+ // Saturation - min - left negative, right positive
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(minVal, IntegerT{4});
+ assert(sum == minVal);
+ }
+
+ // Saturation - max - left postitive, right negative
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(maxVal, IntegerT{-4});
+ assert(sum == maxVal);
+ }
+
+ 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::sub_sat(minVal, maxVal)));
+
+ // No saturation
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(IntegerT{3}, IntegerT{1});
+ assert(sum == IntegerT{2});
+ }
+
+ // Saturation - min only
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(minVal, IntegerT{4});
+ assert(sum == minVal);
+ }
+
+ 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;
+}
+
+constexpr void cppreference_test() {
+ {
+ static_assert(
+ "" && (std::sub_sat<int>(INT_MIN + 4, 3) == INT_MIN + 1) // not saturated
+ && (std::sub_sat<int>(INT_MIN + 4, 5) == INT_MIN) // saturated
+ && (std::sub_sat<int>(INT_MAX - 4, -3) == INT_MAX - 1) // not saturated
+ && (std::sub_sat<int>(INT_MAX - 4, -5) == INT_MAX) // saturated
+ && (std::sub_sat<unsigned>(4, 3) == 1) // not saturated
+ && (std::sub_sat<unsigned>(4, 5) == 0) // saturated
+ );
+ }
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ cppreference_test();
+
+ return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
new file mode 100644
index 00000000000000..a8e85975ab1a1c
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_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 sub_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 'sub_sat'}}
+ // expected-error@*:* 0-2 {{expected unqualified-id}}
+ [[maybe_unused]] auto sum = std::sub_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>();
+ ttest_constraintest<std::char32_t>();
+
+ return true;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 8ee92909dfa53c..6e7e4c2bb478d7 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1010,10 +1010,7 @@ def add_version_header(tc):
{
"name": "__cpp_lib_saturation_arithmetic",
"values": {"c++26": 202311}, # P0543R3 Saturation arithmetic
- "headers": [
- "numeric" # TODO verify this entry since the paper was underspecified.
- ],
- "unimplemented": True,
+ "headers": ["numeric"],
},
{
"name": "__cpp_lib_scoped_lock",
>From 2c939ccf4da507e6eabcd370966eddb105815670 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 12 Jan 2024 21:19:39 +0200
Subject: [PATCH 02/20] Cleanup
---
.../include/__numeric/saturation_arithmetic.h | 15 --------
.../numeric.ops.sat/add_sat.pass.cpp | 35 ++-----------------
.../numeric.ops.sat/div_sat.pass.cpp | 14 ++------
.../numeric.ops.sat/mul_sat.pass.cpp | 18 ++--------
.../numeric.ops.sat/saturate_cast.pass.cpp | 29 ++-------------
.../numeric.ops.sat/sub_sat.pass.cpp | 18 ++--------
6 files changed, 11 insertions(+), 118 deletions(-)
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 61ae35f3ee02d5..ec265435eb6a64 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -25,17 +25,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 26
template <typename _Tp>
-// concept __libcpp_standard_integer = __libcpp_unsigned_integer<_Tp> || __libcpp_signed_integer<_Tp>;
-// concept __libcpp_standard_integer =
-// __libcpp_unsigned_integer<remove_cv<_Tp>> || __libcpp_signed_integer<remove_cv<_Tp>>;
concept __libcpp_standard_integer = __libcpp_unsigned_integer<decay_t<_Tp>> || __libcpp_signed_integer<decay_t<_Tp>>;
template <__libcpp_standard_integer _Tp>
// requires __libcpp_standard_integer<_Tp>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
- // builtins: clang/docs/LanguageExtensions.rst
- // builtins:
- // https://github.com/llvm/llvm-project/blob/7b45c549670a8e8b6fe90f4382b0699dd20707d3/clang/docs/LanguageExtensions.rst#L3500
if (_Tp __sum; !__builtin_add_overflow(__x, __y, &__sum))
return __sum;
// Handle overflow
@@ -80,12 +74,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
return std::numeric_limits<_Tp>::max();
} else {
// Signed multiplication overflow
- // if (__x > 0 && __y > 0)
- // // Overflows if (x > 0 && y > 0)
- // return std::numeric_limits<_Tp>::max();
- // else if (__y > 0)
- // // Overflows if (x > 0 && y < 0)
- // return std::numeric_limits<_Tp>::max();
if (__x > 0) {
if (__y > 0)
// Overflows if (x > 0 && y > 0)
@@ -116,9 +104,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
template <__libcpp_standard_integer _Rp, __libcpp_standard_integer _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
- // if (std::in_range<_Rp>(__x)) {
- // return _Rp{__x};
- // }
// Handle overflow
if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
return std::numeric_limits<_Rp>::min();
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
index 2964ce11d8b654..ccd3594fd3a652 100644
--- 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
@@ -74,13 +74,13 @@ constexpr bool test_unsigned() {
}
constexpr bool test() {
- // signed
+ // Signed
test_signed<signed char>();
test_signed<short int>();
test_signed<int>();
test_signed<long int>();
test_signed<long long int>();
- // unsigned
+ // Unsigned
test_unsigned<unsigned char>();
test_unsigned<unsigned short int>();
test_unsigned<unsigned int>();
@@ -90,40 +90,9 @@ constexpr bool test() {
return true;
}
-// ADDITIONAL_COMPILE_FLAGS: -Wno-constant-conversion
-
-constexpr void cppreference_test() {
- {
- constexpr int a = std::add_sat(3, 4); // no saturation occurs, T = int
- static_assert(a == 7);
-
- constexpr unsigned char b = std::add_sat<unsigned char>(UCHAR_MAX, 4); // saturated
- static_assert(b == UCHAR_MAX);
-
- constexpr unsigned char c = std::add_sat(UCHAR_MAX, 4); // not saturated, T = int
- // add_sat(int, int) returns int tmp == 259,
- // then assignment truncates 259 % 256 == 3
- static_assert(c == 3);
-
- // unsigned char d = std::add_sat(252, c); // Error: inconsistent deductions for T
-
- constexpr unsigned char e = std::add_sat<unsigned char>(251, a); // saturated
- static_assert(e == UCHAR_MAX);
- // 251 is of type T = unsigned char, `a` is converted to unsigned char value;
- // might yield an int -> unsigned char conversion warning for `a`
-
- constexpr signed char f = std::add_sat<signed char>(-123, -3); // not saturated
- static_assert(f == -126);
-
- constexpr signed char g = std::add_sat<signed char>(-123, -13); // saturated
- static_assert(g == std::numeric_limits<signed char>::min()); // g == -128
- }
-}
-
int main(int, char**) {
test();
static_assert(test());
- cppreference_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
index 0411002d7c199e..d2891f77c564fa 100644
--- 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
@@ -74,13 +74,13 @@ constexpr bool test_unsigned() {
}
constexpr bool test() {
- // signed
+ // Signed
test_signed<signed char>();
test_signed<short int>();
test_signed<int>();
test_signed<long int>();
test_signed<long long int>();
- // unsigned
+ // Unsigned
test_unsigned<unsigned char>();
test_unsigned<unsigned short int>();
test_unsigned<unsigned int>();
@@ -90,19 +90,9 @@ constexpr bool test() {
return true;
}
-constexpr void cppreference_test() {
- {
- static_assert("" && (std::div_sat<int>(6, 3) == 2) // not saturated
- && (std::div_sat<int>(INT_MIN, -1) == INT_MAX) // saturated
- && (std::div_sat<unsigned>(6, 3) == 2) // not saturated
- );
- }
-}
-
int main(int, char**) {
test();
static_assert(test());
- cppreference_test();
return 0;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 1192b6b4a61dc0..2fbecd2fa55ff6 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -81,13 +81,13 @@ constexpr bool test_unsigned() {
}
constexpr bool test() {
- // signed
+ // Signed
test_signed<signed char>();
test_signed<short int>();
test_signed<int>();
test_signed<long int>();
test_signed<long long int>();
- // unsigned
+ // Unsigned
test_unsigned<unsigned char>();
test_unsigned<unsigned short int>();
test_unsigned<unsigned int>();
@@ -97,23 +97,9 @@ constexpr bool test() {
return true;
}
-constexpr void cppreference_test() {
- {
- static_assert(
- "" && (std::mul_sat<int>(2, 3) == 6) // not saturated
- && (std::mul_sat<int>(INT_MAX / 2, 3) == INT_MAX) // saturated
- && (std::mul_sat<int>(-2, 3) == -6) // not saturated
- && (std::mul_sat<int>(INT_MIN / -2, -3) == INT_MIN) // saturated
- && (std::mul_sat<unsigned>(2, 3) == 6) // not saturated
- && (std::mul_sat<unsigned>(UINT_MAX / 2, 3) == UINT_MAX) // saturated
- );
- }
-}
-
int main(int, char**) {
test();
static_assert(test());
- cppreference_test();
return 0;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 889b1864f128e5..7e75903c46b109 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -18,8 +18,6 @@
#include <limits>
#include <numeric>
-#include <print>
-
template <typename IntegerResultT, typename IntegerT>
constexpr bool test_signed_notsaturated() {
constexpr auto minVal = std::numeric_limits<IntegerT>::min();
@@ -77,7 +75,7 @@ constexpr bool test_unsigned_saturated() {
}
constexpr bool test() {
- // signed
+ // Signed
test_signed_notsaturated<long long int, signed char>();
test_signed_notsaturated<long long int, short int>();
test_signed_notsaturated<long long int, int>();
@@ -88,7 +86,7 @@ constexpr bool test() {
test_signed_saturated<int, long long int>();
test_signed_saturated<long int, long long int>();
test_signed_saturated<long long int, long long int>();
- // unsigned
+ // Unsigned
test_unsigned_notsaturated<unsigned long long int, unsigned char>();
test_unsigned_notsaturated<unsigned long long int, unsigned short int>();
test_unsigned_notsaturated<unsigned long long int, unsigned int>();
@@ -103,30 +101,9 @@ constexpr bool test() {
return true;
}
-constexpr void cppreference_test() {
- {
- constexpr std::int16_t x1{696};
-
- constexpr std::int8_t x2 = std::saturate_cast<std::int8_t>(x1);
- static_assert(x2 == std::numeric_limits<std::int8_t>::max());
-
- constexpr std::uint8_t x3 = std::saturate_cast<std::uint8_t>(x1);
- static_assert(x3 == std::numeric_limits<std::uint8_t>::max());
-
- constexpr std::int16_t y1{-696};
-
- constexpr std::int8_t y2 = std::saturate_cast<std::int8_t>(y1);
- static_assert(y2 == std::numeric_limits<std::int8_t>::min());
-
- constexpr std::uint8_t y3 = std::saturate_cast<std::uint8_t>(y1);
- static_assert(y3 == 0);
- }
-}
-
int main(int, char**) {
test();
- // static_assert(test());
- cppreference_test();
+ static_assert(test());
return 0;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index a3f0d175ec350d..be337dae275d91 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -69,13 +69,13 @@ constexpr bool test_unsigned() {
}
constexpr bool test() {
- // signed
+ // Signed
test_signed<signed char>();
test_signed<short int>();
test_signed<int>();
test_signed<long int>();
test_signed<long long int>();
- // unsigned
+ // Unsigned
test_unsigned<unsigned char>();
test_unsigned<unsigned short int>();
test_unsigned<unsigned int>();
@@ -85,23 +85,9 @@ constexpr bool test() {
return true;
}
-constexpr void cppreference_test() {
- {
- static_assert(
- "" && (std::sub_sat<int>(INT_MIN + 4, 3) == INT_MIN + 1) // not saturated
- && (std::sub_sat<int>(INT_MIN + 4, 5) == INT_MIN) // saturated
- && (std::sub_sat<int>(INT_MAX - 4, -3) == INT_MAX - 1) // not saturated
- && (std::sub_sat<int>(INT_MAX - 4, -5) == INT_MAX) // saturated
- && (std::sub_sat<unsigned>(4, 3) == 1) // not saturated
- && (std::sub_sat<unsigned>(4, 5) == 0) // saturated
- );
- }
-}
-
int main(int, char**) {
test();
static_assert(test());
- cppreference_test();
return 0;
}
>From f5db95e38044caaf7164dd9bb73ba2250f0da9ae Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 12 Jan 2024 21:30:22 +0200
Subject: [PATCH 03/20] Fixed "code formatter" error
---
libcxx/modules/std/numeric.inc | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/modules/std/numeric.inc b/libcxx/modules/std/numeric.inc
index 0800319de820d9..3bc7b231681584 100644
--- a/libcxx/modules/std/numeric.inc
+++ b/libcxx/modules/std/numeric.inc
@@ -58,10 +58,10 @@ export namespace std {
#if _LIBCPP_STD_VER >= 26
// [numeric.sat], saturation arithmetic
using std::add_sat;
- using std::sub_sat;
- using std::mul_sat;
using std::div_sat;
+ using std::mul_sat;
using std::saturate_cast;
+ using std::sub_sat;
#endif
} // namespace std
>From db3e72e22de261712f924f62929a6d499d1827d6 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Fri, 12 Jan 2024 23:28:21 +0200
Subject: [PATCH 04/20] Cleanup
---
libcxx/include/__numeric/saturation_arithmetic.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index ec265435eb6a64..6d13ec5b3fd5bc 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -28,7 +28,6 @@ template <typename _Tp>
concept __libcpp_standard_integer = __libcpp_unsigned_integer<decay_t<_Tp>> || __libcpp_signed_integer<decay_t<_Tp>>;
template <__libcpp_standard_integer _Tp>
-// requires __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;
>From 30a159394380814e603ee1720f7e62d911e284b8 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 13 Jan 2024 21:27:23 +0200
Subject: [PATCH 05/20] WIP: prepare to address comments
---
libcxx/include/__concepts/arithmetic.h | 4 ++++
.../include/__numeric/saturation_arithmetic.h | 15 +++++----------
.../numeric.ops.sat/add_sat.verify.cpp | 12 ++++++++----
.../numeric.ops.sat/div_sat.verify.cpp | 12 ++++++++----
.../numeric.ops.sat/mul_sat.verify.cpp | 12 ++++++++----
.../numeric.ops.sat/saturate_cast.verify.cpp | 18 +++++++++++++-----
.../numeric.ops.sat/sub_sat.verify.cpp | 9 ++++++---
7 files changed, 52 insertions(+), 30 deletions(-)
diff --git a/libcxx/include/__concepts/arithmetic.h b/libcxx/include/__concepts/arithmetic.h
index f41e4c9f2747cc..f7efd31cf7792d 100644
--- a/libcxx/include/__concepts/arithmetic.h
+++ b/libcxx/include/__concepts/arithmetic.h
@@ -42,9 +42,13 @@ concept floating_point = is_floating_point_v<_Tp>;
template <class _Tp>
concept __libcpp_unsigned_integer = __libcpp_is_unsigned_integer<_Tp>::value;
+
template <class _Tp>
concept __libcpp_signed_integer = __libcpp_is_signed_integer<_Tp>::value;
+template <typename _Tp>
+concept __libcpp_integer = __libcpp_unsigned_integer<_Tp> || __libcpp_signed_integer<_Tp>;
+
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 6d13ec5b3fd5bc..8069d2574a91b2 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -12,7 +12,6 @@
#include <__concepts/arithmetic.h>
#include <__config>
-#include <__type_traits/decay.h>
#include <__utility/cmp.h>
#include <limits>
@@ -23,11 +22,7 @@
_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>
+template <__libcpp_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;
@@ -45,7 +40,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
}
}
-template <__libcpp_standard_integer _Tp>
+template <__libcpp_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;
@@ -64,7 +59,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
}
}
-template <__libcpp_standard_integer _Tp>
+template <__libcpp_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;
@@ -88,7 +83,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
}
}
-template <__libcpp_standard_integer _Tp>
+template <__libcpp_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>) {
@@ -101,7 +96,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
}
}
-template <__libcpp_standard_integer _Rp, __libcpp_standard_integer _Tp>
+template <__libcpp_integer _Rp, __libcpp_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()))
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
index b05b30abea6fdb..2c40d28e50b25a 100644
--- 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
@@ -21,8 +21,8 @@
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}}
+ // expected-error@*:* 0-9 {{no matching function for call to 'add_sat'}}
+ // expected-error@*:* 0-3 {{expected unqualified-id}}
[[maybe_unused]] auto sum = std::add_sat(IntegerT{3}, IntegerT{4});
}
@@ -32,8 +32,12 @@ constexpr bool test() {
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_constraint<wchar_t>();
#endif
- test_constraint<std::char16_t>();
- test_constraint<std::char32_t>();
+ test_constraint<char8_t>();
+ test_constraint<char16_t>();
+ test_constraint<char32_t>();
+ test_constraint<float>();
+ test_constraint<double>();
+ test_constraint<long double>();
return true;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
index e8ab8ef0c148f4..65bce891b86655 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
@@ -21,8 +21,8 @@
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 'div_sat'}}
- // expected-error@*:* 0-2 {{expected unqualified-id}}
+ // expected-error@*:* 0-9 {{no matching function for call to 'div_sat'}}
+ // expected-error@*:* 0-3 {{expected unqualified-id}}
[[maybe_unused]] auto sum = std::div_sat(IntegerT{3}, IntegerT{4});
}
@@ -32,8 +32,12 @@ constexpr bool test() {
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_constraint<wchar_t>();
#endif
- test_constraint<std::char16_t>();
- test_constraint<std::char32_t>();
+ test_constraint<char8_t>();
+ test_constraint<char16_t>();
+ test_constraint<char32_t>();
+ test_constraint<float>();
+ test_constraint<double>();
+ test_constraint<long double>();
return true;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
index 8113be2fd23e6b..fbc3c53d8a6c1a 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
@@ -21,8 +21,8 @@
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 'mul_sat'}}
- // expected-error@*:* 0-2 {{expected unqualified-id}}
+ // expected-error@*:* 0-9 {{no matching function for call to 'mul_sat'}}
+ // expected-error@*:* 0-3 {{expected unqualified-id}}
[[maybe_unused]] auto sum = std::mul_sat(IntegerT{3}, IntegerT{4});
}
@@ -32,8 +32,12 @@ constexpr bool test() {
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_constraint<wchar_t>();
#endif
- test_constraint<std::char16_t>();
- test_constraint<std::char32_t>();
+ test_constraint<char8_t>();
+ test_constraint<char16_t>();
+ test_constraint<char32_t>();
+ test_constraint<float>();
+ test_constraint<double>();
+ test_constraint<long double>();
return true;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
index d8895225e7468e..ed2aee5e90c249 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
@@ -21,7 +21,7 @@
template <typename ResultIntegerT, typename IntegerT>
constexpr void test_constraint() {
// expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
- // expected-error@*:* 0-6 {{no matching function for call to 'saturate_cast'}}
+ // expected-error@*:* 0-25 {{no matching function for call to 'saturate_cast'}}
// expected-error@*:* 0-4 {{expected unqualified-id}}
[[maybe_unused]] auto sum = std::saturate_cast<ResultIntegerT>(IntegerT{4});
}
@@ -35,10 +35,18 @@ constexpr bool test() {
test_constraint<wchar_t, int>();
test_constraint<int, wchar_t>();
#endif
- test_constraint<std::char16_t, int>();
- test_constraint<std::char32_t, int>();
- test_constraint<int, std::char16_t>();
- test_constraint<int, std::char32_t>();
+ test_constraint<char8_t, int>();
+ test_constraint<char16_t, int>();
+ test_constraint<char32_t, int>();
+ test_constraint<float, int>();
+ test_constraint<double, int>();
+ test_constraint<long double, int>();
+ test_constraint<int, char8_t>();
+ test_constraint<int, char16_t>();
+ test_constraint<int, char32_t>();
+ test_constraint<int, float>();
+ test_constraint<int, double>();
+ test_constraint<int, long double>();
return true;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
index a8e85975ab1a1c..034889373d156f 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
@@ -21,7 +21,7 @@
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 'sub_sat'}}
+ // expected-error@*:* 0-9 {{no matching function for call to 'sub_sat'}}
// expected-error@*:* 0-2 {{expected unqualified-id}}
[[maybe_unused]] auto sum = std::sub_sat(IntegerT{3}, IntegerT{4});
}
@@ -32,8 +32,11 @@ constexpr bool test() {
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_constraint<wchar_t>();
#endif
- test_constraint<std::char16_t>();
- ttest_constraintest<std::char32_t>();
+ test_constraint<char16_t>();
+ test_constraint<char32_t>();
+ test_constraint<float>();
+ test_constraint<double>();
+ test_constraint<long double>();
return true;
}
>From 2793acbef4e277ee72b854efa77b615402bf72a7 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 13 Jan 2024 22:54:35 +0200
Subject: [PATCH 06/20] WIP: Cleaned up tests
---
.../numeric.ops/numeric.ops.sat/add_sat.verify.cpp | 6 ++----
.../numeric.ops/numeric.ops.sat/div_sat.verify.cpp | 6 ++----
.../numeric.ops/numeric.ops.sat/mul_sat.verify.cpp | 6 ++----
.../numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp | 6 ++----
.../numeric.ops/numeric.ops.sat/sub_sat.verify.cpp | 7 +++----
5 files changed, 11 insertions(+), 20 deletions(-)
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
index 2c40d28e50b25a..7df73043c922bd 100644
--- 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
@@ -20,10 +20,8 @@
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-9 {{no matching function for call to 'add_sat'}}
- // expected-error@*:* 0-3 {{expected unqualified-id}}
- [[maybe_unused]] auto sum = std::add_sat(IntegerT{3}, IntegerT{4});
+ // expected-error@*:* 8-9 {{no matching function for call to 'add_sat'}}
+ [[maybe_unused]] auto sum = std::add_sat(IntegerT{}, IntegerT{});
}
constexpr bool test() {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
index 65bce891b86655..5db6efd4ce397e 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
@@ -20,10 +20,8 @@
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-9 {{no matching function for call to 'div_sat'}}
- // expected-error@*:* 0-3 {{expected unqualified-id}}
- [[maybe_unused]] auto sum = std::div_sat(IntegerT{3}, IntegerT{4});
+ // expected-error@*:* 8-9 {{no matching function for call to 'div_sat'}}
+ [[maybe_unused]] auto sum = std::div_sat(IntegerT{}, IntegerT{});
}
constexpr bool test() {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
index fbc3c53d8a6c1a..fcc24c09b7b9b4 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
@@ -20,10 +20,8 @@
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-9 {{no matching function for call to 'mul_sat'}}
- // expected-error@*:* 0-3 {{expected unqualified-id}}
- [[maybe_unused]] auto sum = std::mul_sat(IntegerT{3}, IntegerT{4});
+ // expected-error@*:* 8-9 {{no matching function for call to 'mul_sat'}}
+ [[maybe_unused]] auto sum = std::mul_sat(IntegerT{}, IntegerT{});
}
constexpr bool test() {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
index ed2aee5e90c249..379628b5664841 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
@@ -20,10 +20,8 @@
template <typename ResultIntegerT, typename IntegerT>
constexpr void test_constraint() {
- // expected-error-re@*:* 0-2 {{constant expression evaluates to {{.*}} which cannot be narrowed to type {{.*}}}}
- // expected-error@*:* 0-25 {{no matching function for call to 'saturate_cast'}}
- // expected-error@*:* 0-4 {{expected unqualified-id}}
- [[maybe_unused]] auto sum = std::saturate_cast<ResultIntegerT>(IntegerT{4});
+ // expected-error@*:* 16-18 {{no matching function for call to 'saturate_cast'}}
+ [[maybe_unused]] auto sum = std::saturate_cast<ResultIntegerT>(IntegerT{});
}
constexpr bool test() {
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
index 034889373d156f..1ca5ca329b6c9a 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
@@ -20,10 +20,8 @@
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-9 {{no matching function for call to 'sub_sat'}}
- // expected-error@*:* 0-2 {{expected unqualified-id}}
- [[maybe_unused]] auto sum = std::sub_sat(IntegerT{3}, IntegerT{4});
+ // expected-error@*:* 8-9 {{no matching function for call to 'sub_sat'}}
+ [[maybe_unused]] auto sum = std::sub_sat(IntegerT{}, IntegerT{});
}
constexpr bool test() {
@@ -32,6 +30,7 @@ constexpr bool test() {
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_constraint<wchar_t>();
#endif
+ test_constraint<char8_t>();
test_constraint<char16_t>();
test_constraint<char32_t>();
test_constraint<float>();
>From 540cfec4a90bf628d6767b255218117f0b107e08 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 13 Jan 2024 23:39:24 +0200
Subject: [PATCH 07/20] WIP: Test `constexpr`
---
.../numeric.ops.sat/div_sat.assert.pass.cpp | 50 +++++++++++++------
1 file changed, 36 insertions(+), 14 deletions(-)
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
index 98616d2c1c427c..971ea6a0a61468 100644
--- 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
@@ -24,24 +24,46 @@
#include "check_assertion.h"
+#define ASSERT_CONSTEXPR(Expr) static_assert(__builtin_constant_p(Expr))
+#define ASSERT_NOT_CONSTEXPR(Expr) static_assert(!__builtin_constant_p(Expr));
+
template <typename IntegerT>
-constexpr void test() {
+void test_assertion() {
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>();
+template <typename IntegerT>
+void test_constexpr() {
+ ASSERT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{84}));
+ ASSERT_NOT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{0}));
+}
+
+bool test() {
+ // Signed
+ test_assertion<signed char>();
+ test_assertion<short int>();
+ test_assertion<int>();
+ test_assertion<long int>();
+ test_assertion<long long int>();
+ // Unsigned
+ test_assertion<unsigned char>();
+ test_assertion<unsigned short int>();
+ test_assertion<unsigned int>();
+ test_assertion<unsigned long int>();
+ test_assertion<unsigned long long int>();
+
+ // Signed
+ test_constexpr<signed char>();
+ test_constexpr<short int>();
+ test_constexpr<int>();
+ test_constexpr<long int>();
+ test_constexpr<long long int>();
+ // Unsigned
+ test_constexpr<unsigned char>();
+ test_constexpr<unsigned short int>();
+ test_constexpr<unsigned int>();
+ test_constexpr<unsigned long int>();
+ test_constexpr<unsigned long long int>();
return true;
}
>From 83fff64955a2ef33aff63ce55bd18bd436de49b2 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 00:36:53 +0200
Subject: [PATCH 08/20] WIP: Optimized `saturate_cast`
---
.../include/__numeric/saturation_arithmetic.h | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 8069d2574a91b2..9514413453892a 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -22,6 +22,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 26
+
template <__libcpp_integer _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
if (_Tp __sum; !__builtin_add_overflow(__x, __y, &__sum))
@@ -98,13 +99,17 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
template <__libcpp_integer _Rp, __libcpp_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);
+ // Saturation is impossible.
+ if constexpr (std::cmp_less_equal(std::numeric_limits<_Rp>::min(), std::numeric_limits<_Tp>::min()) &&
+ std::cmp_greater_equal(std::numeric_limits<_Rp>::max(), std::numeric_limits<_Tp>::max()))
+ return static_cast<_Rp>(__x);
+ else {
+ // Handle overflow
+ if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
+ return std::numeric_limits<_Rp>::min();
+ // x >= std::numeric_limits<_Rp>::max()
+ return std::numeric_limits<_Rp>::max();
+ }
}
#endif // _LIBCPP_STD_VER >= 26
>From 2cab21ac5e83287a90ed2e73d9ee079a846b4f3c Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 10:11:05 +0200
Subject: [PATCH 09/20] Renamed test
---
.../numeric.ops.sat/div_sat.assert.pass.cpp | 22 +++++++++----------
1 file changed, 11 insertions(+), 11 deletions(-)
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
index 971ea6a0a61468..124affc9a8b6cb 100644
--- 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
@@ -28,7 +28,7 @@
#define ASSERT_NOT_CONSTEXPR(Expr) static_assert(!__builtin_constant_p(Expr));
template <typename IntegerT>
-void test_assertion() {
+void test_runtime_assertion() {
TEST_LIBCPP_ASSERT_FAILURE((void)std::div_sat(IntegerT{3}, IntegerT{0}), "Division by 0 is undefined");
}
@@ -40,17 +40,17 @@ void test_constexpr() {
bool test() {
// Signed
- test_assertion<signed char>();
- test_assertion<short int>();
- test_assertion<int>();
- test_assertion<long int>();
- test_assertion<long long int>();
+ test_runtime_assertion<signed char>();
+ test_runtime_assertion<short int>();
+ test_runtime_assertion<int>();
+ test_runtime_assertion<long int>();
+ test_runtime_assertion<long long int>();
// Unsigned
- test_assertion<unsigned char>();
- test_assertion<unsigned short int>();
- test_assertion<unsigned int>();
- test_assertion<unsigned long int>();
- test_assertion<unsigned long long int>();
+ test_runtime_assertion<unsigned char>();
+ test_runtime_assertion<unsigned short int>();
+ test_runtime_assertion<unsigned int>();
+ test_runtime_assertion<unsigned long int>();
+ test_runtime_assertion<unsigned long long int>();
// Signed
test_constexpr<signed char>();
>From 68cbc9f8832a181c19ae1e65d82731d775452313 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 10:57:54 +0200
Subject: [PATCH 10/20] WIP: `add_sat` - edge case tests
---
.../numeric.ops.sat/add_sat.pass.cpp | 65 ++++++++++++++++---
1 file changed, 57 insertions(+), 8 deletions(-)
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
index ccd3594fd3a652..085c3ce7dd8bb9 100644
--- 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
@@ -27,24 +27,55 @@ constexpr bool test_signed() {
// 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{27}, IntegerT{28});
+ assert(sum == IntegerT{55});
}
{
- std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-3}, IntegerT{4});
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-27}, IntegerT{28});
assert(sum == IntegerT{1});
}
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
+ assert(sum == minVal);
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{0});
+ assert(sum == maxVal);
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{1});
+ assert(sum == minVal + IntegerT{1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{-1});
+ assert(sum == maxVal + IntegerT{-1});
+ }
+
// Saturation - max - both arguments positive
{
- std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{4});
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
+ assert(sum == maxVal);
+ }
+
+ {
+ constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+ constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(x, y);
assert(sum == maxVal);
}
// Saturation - min - both arguments negative
{
- std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{-4});
+ constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
+ constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-28};
+
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(x, y);
assert(sum == minVal);
}
@@ -60,13 +91,31 @@ constexpr bool test_unsigned() {
// 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{27}, IntegerT{28});
+ assert(sum == IntegerT{55});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
+ assert(sum == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{1});
+ assert(sum == IntegerT{1});
}
// Saturation - max only
{
- std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{4});
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
+ assert(sum == maxVal);
+ }
+
+ {
+ constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+ constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(x, y);
assert(sum == maxVal);
}
>From e284255f28fdee5c6d91a067b21a73872a5f8327 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 11:14:38 +0200
Subject: [PATCH 11/20] WIP: Added extended integers types to tests
---
.../numeric.ops.sat/add_sat.pass.cpp | 8 ++++++-
.../numeric.ops.sat/div_sat.pass.cpp | 6 +++++
.../numeric.ops.sat/mul_sat.pass.cpp | 6 +++++
.../numeric.ops.sat/saturate_cast.pass.cpp | 24 +++++++++++++++++++
.../numeric.ops.sat/sub_sat.pass.cpp | 6 +++++
5 files changed, 49 insertions(+), 1 deletion(-)
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
index 085c3ce7dd8bb9..9dbaca98327142 100644
--- 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
@@ -95,7 +95,7 @@ constexpr bool test_unsigned() {
assert(sum == IntegerT{55});
}
- {
+ {
std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
assert(sum == IntegerT{0});
}
@@ -129,12 +129,18 @@ constexpr bool test() {
test_signed<int>();
test_signed<long int>();
test_signed<long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_signed<__int128_t>();
+#endif
// 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>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_unsigned<__uint128_t>();
+#endif
return true;
}
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
index d2891f77c564fa..1b3a84c8a41229 100644
--- 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
@@ -80,12 +80,18 @@ constexpr bool test() {
test_signed<int>();
test_signed<long int>();
test_signed<long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_signed<__int128_t>();
+#endif
// 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>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_unsigned<__uint128_t>();
+#endif
return true;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 2fbecd2fa55ff6..2f68d4f6ed4d4e 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -87,12 +87,18 @@ constexpr bool test() {
test_signed<int>();
test_signed<long int>();
test_signed<long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_signed<__int128_t>();
+#endif
// 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>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_unsigned<__uint128_t>();
+#endif
return true;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 7e75903c46b109..710d0ade4cb433 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -86,6 +86,18 @@ constexpr bool test() {
test_signed_saturated<int, long long int>();
test_signed_saturated<long int, long long int>();
test_signed_saturated<long long int, long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_signed_notsaturated<__int128_t, signed char>();
+ test_signed_notsaturated<__int128_t, short int>();
+ test_signed_notsaturated<__int128_t, int>();
+ test_signed_notsaturated<__int128_t, long int>();
+ test_signed_notsaturated<__int128_t, long long int>();
+ test_signed_saturated<signed char, __int128_t>();
+ test_signed_saturated<short int, __int128_t>();
+ test_signed_saturated<int, __int128_t>();
+ test_signed_saturated<long int, __int128_t>();
+ test_signed_saturated<long long int, __int128_t>();
+#endif
// Unsigned
test_unsigned_notsaturated<unsigned long long int, unsigned char>();
test_unsigned_notsaturated<unsigned long long int, unsigned short int>();
@@ -97,6 +109,18 @@ constexpr bool test() {
test_unsigned_saturated<unsigned int, unsigned long long int>();
test_unsigned_saturated<unsigned long int, unsigned long long int>();
test_unsigned_saturated<unsigned long long int, unsigned long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_unsigned_notsaturated<__uint128_t, unsigned char>();
+ test_unsigned_notsaturated<__uint128_t, unsigned short int>();
+ test_unsigned_notsaturated<__uint128_t, unsigned int>();
+ test_unsigned_notsaturated<__uint128_t, unsigned long int>();
+ test_unsigned_notsaturated<__uint128_t, unsigned long long int>();
+ test_unsigned_saturated<unsigned char, __uint128_t>();
+ test_unsigned_saturated<unsigned short int, __uint128_t>();
+ test_unsigned_saturated<unsigned int, __uint128_t>();
+ test_unsigned_saturated<unsigned long int, __uint128_t>();
+ test_unsigned_saturated<unsigned long long int, __uint128_t>();
+#endif
return true;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index be337dae275d91..1a35cd4fdd0609 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -75,12 +75,18 @@ constexpr bool test() {
test_signed<int>();
test_signed<long int>();
test_signed<long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_signed<__int128_t>();
+#endif
// 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>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_unsigned<__uint128_t>();
+#endif
return true;
}
>From fb439785b084b83cf8dadea048c6bc87bd7676f2 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 11:25:17 +0200
Subject: [PATCH 12/20] WIP: Added `check_constexpr.h`
---
.../numeric.ops.sat/add_sat.pass.cpp | 2 +-
.../numeric.ops.sat/div_sat.assert.pass.cpp | 32 ++++---------------
.../numeric.ops.sat/div_sat.pass.cpp | 28 +++++++++++++++-
.../numeric.ops.sat/mul_sat.pass.cpp | 2 +-
.../numeric.ops.sat/saturate_cast.pass.cpp | 2 +-
.../numeric.ops.sat/sub_sat.pass.cpp | 2 +-
libcxx/test/support/check_constexpr.h | 21 ++++++++++++
7 files changed, 59 insertions(+), 30 deletions(-)
create mode 100644 libcxx/test/support/check_constexpr.h
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
index 9dbaca98327142..d71599d87e02c7 100644
--- 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
@@ -146,7 +146,7 @@ constexpr bool test() {
}
int main(int, char**) {
- test();
+ assert(test());
static_assert(test());
return 0;
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
index 124affc9a8b6cb..a67b714cf65fb2 100644
--- 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
@@ -18,24 +18,13 @@
// constexpr T div_sat(T x, T y) noexcept; // freestanding
#include <cassert>
-#include <concepts>
-#include <limits>
#include <numeric>
#include "check_assertion.h"
-#define ASSERT_CONSTEXPR(Expr) static_assert(__builtin_constant_p(Expr))
-#define ASSERT_NOT_CONSTEXPR(Expr) static_assert(!__builtin_constant_p(Expr));
-
template <typename IntegerT>
void test_runtime_assertion() {
- TEST_LIBCPP_ASSERT_FAILURE((void)std::div_sat(IntegerT{3}, IntegerT{0}), "Division by 0 is undefined");
-}
-
-template <typename IntegerT>
-void test_constexpr() {
- ASSERT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{84}));
- ASSERT_NOT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{0}));
+ TEST_LIBCPP_ASSERT_FAILURE((void)std::div_sat(IntegerT{27}, IntegerT{0}), "Division by 0 is undefined");
}
bool test() {
@@ -45,25 +34,18 @@ bool test() {
test_runtime_assertion<int>();
test_runtime_assertion<long int>();
test_runtime_assertion<long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_runtime_assertion<__int128_t>();
+#endif
// Unsigned
test_runtime_assertion<unsigned char>();
test_runtime_assertion<unsigned short int>();
test_runtime_assertion<unsigned int>();
test_runtime_assertion<unsigned long int>();
test_runtime_assertion<unsigned long long int>();
-
- // Signed
- test_constexpr<signed char>();
- test_constexpr<short int>();
- test_constexpr<int>();
- test_constexpr<long int>();
- test_constexpr<long long int>();
- // Unsigned
- test_constexpr<unsigned char>();
- test_constexpr<unsigned short int>();
- test_constexpr<unsigned int>();
- test_constexpr<unsigned long int>();
- test_constexpr<unsigned long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_runtime_assertion<__uint128_t>();
+#endif
return true;
}
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
index 1b3a84c8a41229..506dd6e15ab623 100644
--- 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
@@ -18,6 +18,8 @@
#include <limits>
#include <numeric>
+#include "check_constexpr.h"
+
template <typename IntegerT>
constexpr bool test_signed() {
constexpr auto minVal = std::numeric_limits<IntegerT>::min();
@@ -73,6 +75,12 @@ constexpr bool test_unsigned() {
return true;
}
+template <typename IntegerT>
+void test_constexpr() {
+ TEST_EXPRESSION_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{84}));
+ TEST_EXPRESSION_NOT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{0}));
+}
+
constexpr bool test() {
// Signed
test_signed<signed char>();
@@ -96,9 +104,27 @@ constexpr bool test() {
return true;
}
+bool test_constexpr() {
+ // Signed
+ test_constexpr<signed char>();
+ test_constexpr<short int>();
+ test_constexpr<int>();
+ test_constexpr<long int>();
+ test_constexpr<long long int>();
+ // Unsigned
+ test_constexpr<unsigned char>();
+ test_constexpr<unsigned short int>();
+ test_constexpr<unsigned int>();
+ test_constexpr<unsigned long int>();
+ test_constexpr<unsigned long long int>();
+
+ return true;
+}
+
int main(int, char**) {
- test();
+ assert(test());
static_assert(test());
+ assert(test_constexpr());
return 0;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 2f68d4f6ed4d4e..1e502e1cbaff3b 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -104,7 +104,7 @@ constexpr bool test() {
}
int main(int, char**) {
- test();
+ assert(test());
static_assert(test());
return 0;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index 710d0ade4cb433..f7c1cb4dfc09d2 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -126,7 +126,7 @@ constexpr bool test() {
}
int main(int, char**) {
- test();
+ assert(test());
static_assert(test());
return 0;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index 1a35cd4fdd0609..12c14860560a87 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -92,7 +92,7 @@ constexpr bool test() {
}
int main(int, char**) {
- test();
+ assert(test());
static_assert(test());
return 0;
diff --git a/libcxx/test/support/check_constexpr.h b/libcxx/test/support/check_constexpr.h
new file mode 100644
index 00000000000000..54a6c6caf7c945
--- /dev/null
+++ b/libcxx/test/support/check_constexpr.h
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_SUPPORT_CHECK_CONSTEXPR_H
+#define TEST_SUPPORT_CHECK_CONSTEXPR_H
+
+#include "test_macros.h"
+
+#if TEST_STD_VER < 11
+# error "C++11 or greater is required to use this header"
+#endif
+
+#define TEST_EXPRESSION_CONSTEXPR(expr) static_assert(__builtin_constant_p(expr))
+#define TEST_EXPRESSION_NOT_CONSTEXPR(expr) static_assert(!__builtin_constant_p(expr));
+
+#endif // TEST_SUPPORT_CHECK_CONSTEXPR_H
>From 39837d65d9cf5e6c78ebaf3a00c3d36be5dd32dd Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 12:18:07 +0200
Subject: [PATCH 13/20] WIP: Fixed code formatter error
---
libcxx/include/__numeric/saturation_arithmetic.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 9514413453892a..75dd540a84e689 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -108,7 +108,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
return std::numeric_limits<_Rp>::min();
// x >= std::numeric_limits<_Rp>::max()
- return std::numeric_limits<_Rp>::max();
+ return std::numeric_limits<_Rp>::max();
}
}
>From 3fbec0109f54c8006da57d852e24d0fbe5814f68 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 14 Jan 2024 12:50:25 +0200
Subject: [PATCH 14/20] WIP: Addtional tests `sat_sub`
---
.../numeric.ops.sat/div_sat.pass.cpp | 24 +++++-----
.../numeric.ops.sat/mul_sat.pass.cpp | 28 ++++++------
.../numeric.ops.sat/sub_sat.pass.cpp | 44 ++++++++++++++-----
3 files changed, 60 insertions(+), 36 deletions(-)
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
index 506dd6e15ab623..c7992bc72e0299 100644
--- 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
@@ -29,24 +29,24 @@ constexpr bool test_signed() {
// No saturation
{
- std::same_as<IntegerT> decltype(auto) div = std::div_sat(IntegerT{3}, IntegerT{4});
- assert(div == IntegerT{0});
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{3}, IntegerT{4});
+ assert(quot == IntegerT{0});
}
{
- std::same_as<IntegerT> decltype(auto) div = std::div_sat(maxVal, minVal);
- assert(div == (maxVal / minVal));
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, minVal);
+ assert(quot == (maxVal / minVal));
}
{
- std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, maxVal);
- assert(div == (minVal / maxVal));
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
+ assert(quot == (minVal / maxVal));
}
// Saturation - max only
{
- std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, IntegerT{-1});
- assert(div == maxVal);
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, IntegerT{-1});
+ assert(quot == maxVal);
}
return true;
@@ -61,13 +61,13 @@ constexpr bool test_unsigned() {
// No saturation
{
- std::same_as<IntegerT> decltype(auto) div = std::div_sat(IntegerT{3}, IntegerT{4});
- assert(div == IntegerT{0});
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{3}, IntegerT{4});
+ assert(quot == IntegerT{0});
}
{
- std::same_as<IntegerT> decltype(auto) div = std::div_sat(minVal, maxVal);
- assert(div == (minVal / maxVal));
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
+ assert(quot == (minVal / maxVal));
}
// Unsigned integer devision never overflow
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 1e502e1cbaff3b..1f4d0ab518bbea 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -27,32 +27,32 @@ constexpr bool test_signed() {
// No saturation
{
- std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(IntegerT{3}, IntegerT{4});
- assert(sum == IntegerT{12});
+ std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{3}, IntegerT{4});
+ assert(prod == IntegerT{12});
}
// Saturation - max - both arguments positive
{
- std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{4});
- assert(sum == maxVal);
+ std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{4});
+ assert(prod == maxVal);
}
// Saturation - max - both arguments negative
{
- std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, IntegerT{-4});
- assert(sum == maxVal);
+ std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{-4});
+ assert(prod == maxVal);
}
// Saturation - min - left positive, right negative
{
- std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{-4});
- assert(sum == minVal);
+ std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{-4});
+ assert(prod == minVal);
}
// Saturation - min - left negative, right positive
{
- std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(minVal, IntegerT{4});
- assert(sum == minVal);
+ std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(minVal, IntegerT{4});
+ assert(prod == minVal);
}
return true;
@@ -67,14 +67,14 @@ constexpr bool test_unsigned() {
// No saturation
{
- std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(IntegerT{3}, IntegerT{4});
- assert(sum == IntegerT{12});
+ std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(IntegerT{3}, IntegerT{4});
+ assert(prod == IntegerT{12});
}
// Saturation
{
- std::same_as<IntegerT> decltype(auto) sum = std::mul_sat(maxVal, IntegerT{4});
- assert(sum == maxVal);
+ std::same_as<IntegerT> decltype(auto) prod = std::mul_sat(maxVal, IntegerT{4});
+ assert(prod == maxVal);
}
return true;
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index 12c14860560a87..2c5fcf6ec0f699 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -27,20 +27,36 @@ constexpr bool test_signed() {
// No saturation
{
- std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(IntegerT{3}, IntegerT{4});
- assert(sum == IntegerT{-1});
+ std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(IntegerT{3}, IntegerT{4});
+ assert(diff == IntegerT{-1});
}
// Saturation - min - left negative, right positive
{
- std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(minVal, IntegerT{4});
- assert(sum == minVal);
+ std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(minVal, IntegerT{4});
+ assert(diff == minVal);
+ }
+
+ {
+ constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
+ constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+ std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(x, y);
+ assert(diff == minVal);
}
// Saturation - max - left postitive, right negative
{
- std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(maxVal, IntegerT{-4});
- assert(sum == maxVal);
+ std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(maxVal, IntegerT{-4});
+ assert(diff == maxVal);
+ }
+
+ {
+ constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{28};
+ constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{27};
+
+ std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(x, y);
+ assert(diff == maxVal);
}
return true;
@@ -55,14 +71,22 @@ constexpr bool test_unsigned() {
// No saturation
{
- std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(IntegerT{3}, IntegerT{1});
- assert(sum == IntegerT{2});
+ std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(IntegerT{3}, IntegerT{1});
+ assert(diff == IntegerT{2});
}
// Saturation - min only
{
- std::same_as<IntegerT> decltype(auto) sum = std::sub_sat(minVal, IntegerT{4});
- assert(sum == minVal);
+ std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(minVal, IntegerT{4});
+ assert(diff == minVal);
+ }
+
+ {
+ constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{27};
+ constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+ std::same_as<IntegerT> decltype(auto) diff = std::sub_sat(x, y);
+ assert(diff == minVal);
}
return true;
>From a77d6035e1f984109afcd13c55d8c0cf7681cc61 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Tue, 16 Jan 2024 14:10:44 +0200
Subject: [PATCH 15/20] WIP: `add_sat` - additional test cases
---
.../numeric.ops.sat/add_sat.pass.cpp | 66 +++++++++++++++++--
1 file changed, 61 insertions(+), 5 deletions(-)
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
index d71599d87e02c7..29534092b484a3 100644
--- 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
@@ -25,7 +25,54 @@ constexpr bool test_signed() {
static_assert(noexcept(std::div_sat(minVal, maxVal)));
- // No saturation
+ // No saturation - (-1, 0, 1)
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{0});
+ assert(sum == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{1});
+ assert(sum == IntegerT{1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{0});
+ assert(sum == IntegerT{1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{-1});
+ assert(sum == IntegerT{-1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-1}, IntegerT{0});
+ assert(sum == IntegerT{-1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{1});
+ assert(sum == IntegerT{2});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{-1});
+ assert(sum == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-1}, IntegerT{1});
+ assert(sum == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{-1}, IntegerT{-1});
+ assert(sum == IntegerT{-2});
+ }
+
+ // No saturation - (any value)
+
{
std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
assert(sum == IntegerT{55});
@@ -36,19 +83,21 @@ constexpr bool test_signed() {
assert(sum == IntegerT{1});
}
+ // No saturation - (min, -1, 0, 1, max)
+
{
std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
assert(sum == minVal);
}
{
- std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{0});
- assert(sum == maxVal);
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{1});
+ assert(sum == minVal + IntegerT{1});
}
{
- std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{1});
- assert(sum == minVal + IntegerT{1});
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{0});
+ assert(sum == maxVal);
}
{
@@ -63,6 +112,7 @@ constexpr bool test_signed() {
}
{
+ // Large values
constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
@@ -72,6 +122,7 @@ constexpr bool test_signed() {
// Saturation - min - both arguments negative
{
+ // Large values
constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-28};
@@ -79,6 +130,11 @@ constexpr bool test_signed() {
assert(sum == minVal);
}
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, minVal);
+ assert(sum == minVal);
+ }
+
return true;
}
>From faf11737be127c4b4a2b3f8a43c530dd38d6be80 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Tue, 16 Jan 2024 16:30:41 +0200
Subject: [PATCH 16/20] Completed SFINAE tests
---
.../numeric.ops.sat/add_sat.compile.pass.cpp | 75 +++++++++++
.../numeric.ops.sat/add_sat.pass.cpp | 21 ++-
.../numeric.ops.sat/add_sat.verify.cpp | 41 ------
.../numeric.ops.sat/div_sat.compile.pass.cpp | 124 ++++++++++++++++++
.../numeric.ops.sat/div_sat.pass.cpp | 8 --
.../numeric.ops.sat/div_sat.verify.cpp | 41 ------
.../numeric.ops.sat/mul_sat.compile.pass.cpp | 76 +++++++++++
.../numeric.ops.sat/mul_sat.verify.cpp | 41 ------
.../saturate_cast.compile.pass.cpp | 77 +++++++++++
.../numeric.ops.sat/saturate_cast.verify.cpp | 50 -------
.../numeric.ops.sat/sub_sat.compile.pass.cpp | 76 +++++++++++
.../numeric.ops.sat/sub_sat.verify.cpp | 41 ------
libcxx/test/support/check_constexpr.h | 21 ---
13 files changed, 446 insertions(+), 246 deletions(-)
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
delete mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
delete mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
delete mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
delete mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
delete mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
delete mode 100644 libcxx/test/support/check_constexpr.h
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
new file mode 100644
index 00000000000000..56d2cfd4a2585e
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.compile.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <numeric>
+#include <limits>
+
+template <typename T, typename U>
+concept CanDo = requires(T x, U y) {
+ { std::add_sat(x, y) } -> std::same_as<T>;
+};
+
+template <typename T, typename U>
+constexpr void test_constraint_success() {
+ static_assert(CanDo<T, T>);
+ static_assert(!CanDo<U, T>);
+ static_assert(!CanDo<T, U>);
+}
+
+template <typename T>
+constexpr void test_constraint_fail() {
+ using I = int;
+ static_assert(!CanDo<T, T>);
+ static_assert(!CanDo<I, T>);
+ static_assert(!CanDo<T, I>);
+}
+
+constexpr void test() {
+ // Contraint success - Signed
+ using SI = long long int;
+ test_constraint_success<signed char, SI>();
+ test_constraint_success<short int, SI>();
+ test_constraint_success<signed char, SI>();
+ test_constraint_success<short int, SI>();
+ test_constraint_success<int, SI>();
+ test_constraint_success<long int, SI>();
+ test_constraint_success<long long int, int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_constraint_success<__int128_t, SI>();
+#endif
+ // Contraint success - Unsigned
+ using UI = unsigned long long int;
+ test_constraint_success<unsigned char, UI>();
+ test_constraint_success<unsigned short int, UI>();
+ test_constraint_success<unsigned int, UI>();
+ test_constraint_success<unsigned long int, UI>();
+ test_constraint_success<unsigned long long int, unsigned int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_constraint_success<__uint128_t, UI>();
+#endif
+
+ // Contraint failure
+ test_constraint_fail<bool>();
+ test_constraint_fail<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_constraint_fail<wchar_t>();
+#endif
+ test_constraint_fail<char8_t>();
+ test_constraint_fail<char16_t>();
+ test_constraint_fail<char32_t>();
+ test_constraint_fail<float>();
+ test_constraint_fail<double>();
+ test_constraint_fail<long double>();
+}
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
index 29534092b484a3..1fdf647ff606e4 100644
--- 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
@@ -25,7 +25,7 @@ constexpr bool test_signed() {
static_assert(noexcept(std::div_sat(minVal, maxVal)));
- // No saturation - (-1, 0, 1)
+ // No saturation (-1, 0, 1)
{
std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{0});
assert(sum == IntegerT{0});
@@ -71,7 +71,7 @@ constexpr bool test_signed() {
assert(sum == IntegerT{-2});
}
- // No saturation - (any value)
+ // No saturation (any value)
{
std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
@@ -83,7 +83,7 @@ constexpr bool test_signed() {
assert(sum == IntegerT{1});
}
- // No saturation - (min, -1, 0, 1, max)
+ // No saturation (min, -1, 0, 1, max)
{
std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, IntegerT{0});
@@ -120,6 +120,11 @@ constexpr bool test_signed() {
assert(sum == maxVal);
}
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, maxVal);
+ assert(sum == maxVal);
+ }
+
// Saturation - min - both arguments negative
{
// Large values
@@ -161,6 +166,11 @@ constexpr bool test_unsigned() {
assert(sum == IntegerT{1});
}
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, minVal);
+ assert(sum == minVal);
+ }
+
// Saturation - max only
{
std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
@@ -175,6 +185,11 @@ constexpr bool test_unsigned() {
assert(sum == maxVal);
}
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, maxVal);
+ assert(sum == maxVal);
+ }
+
return true;
}
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
deleted file mode 100644
index 7df73043c922bd..00000000000000
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/add_sat.verify.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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@*:* 8-9 {{no matching function for call to 'add_sat'}}
- [[maybe_unused]] auto sum = std::add_sat(IntegerT{}, IntegerT{});
-}
-
-constexpr bool test() {
- test_constraint<bool>();
- test_constraint<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
- test_constraint<wchar_t>();
-#endif
- test_constraint<char8_t>();
- test_constraint<char16_t>();
- test_constraint<char32_t>();
- test_constraint<float>();
- test_constraint<double>();
- test_constraint<long double>();
-
- return true;
-}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
new file mode 100644
index 00000000000000..a91c2d6b5d6fdc
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 div_sat(T x, T y) noexcept; // freestanding
+
+#include <concepts>
+#include <numeric>
+#include <limits>
+#include <type_traits>
+
+// Constraints
+
+template <typename T, typename U>
+concept CanDo = requires(T x, U y) {
+ { std::div_sat(x, y) } -> std::same_as<T>;
+};
+
+template <typename T, typename U>
+constexpr void test_constraint_success() {
+ static_assert(CanDo<T, T>);
+ static_assert(!CanDo<U, T>);
+ static_assert(!CanDo<T, U>);
+}
+
+template <typename T>
+constexpr void test_constraint_fail() {
+ using I = int;
+ static_assert(!CanDo<T, T>);
+ static_assert(!CanDo<I, T>);
+ static_assert(!CanDo<T, I>);
+}
+
+constexpr void test() {
+ // Contraint success - Signed
+ using SI = long long int;
+ test_constraint_success<signed char, SI>();
+ test_constraint_success<short int, SI>();
+ test_constraint_success<signed char, SI>();
+ test_constraint_success<short int, SI>();
+ test_constraint_success<int, SI>();
+ test_constraint_success<long int, SI>();
+ test_constraint_success<long long int, int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_constraint_success<__int128_t, SI>();
+#endif
+ // Contraint success - Unsigned
+ using UI = unsigned long long int;
+ test_constraint_success<unsigned char, UI>();
+ test_constraint_success<unsigned short int, UI>();
+ test_constraint_success<unsigned int, UI>();
+ test_constraint_success<unsigned long int, UI>();
+ test_constraint_success<unsigned long long int, unsigned int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_constraint_success<__uint128_t, UI>();
+#endif
+
+ // Contraint failure
+ test_constraint_fail<bool>();
+ test_constraint_fail<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_constraint_fail<wchar_t>();
+#endif
+ test_constraint_fail<char8_t>();
+ test_constraint_fail<char16_t>();
+ test_constraint_fail<char32_t>();
+ test_constraint_fail<float>();
+ test_constraint_fail<double>();
+ test_constraint_fail<long double>();
+}
+
+// Check precondition: not constexpr when `div_sat` by zero
+
+template <auto N>
+using QuotT = std::integral_constant<decltype(N), std::div_sat(N, N)>;
+
+template <auto N>
+QuotT<N> div_by_zero();
+
+template <auto N>
+concept CanDivByZero = requires { div_by_zero<N>(); };
+
+static_assert(!CanDivByZero<static_cast<signed char>(0)>);
+static_assert(!CanDivByZero<static_cast<short int>(0)>);
+static_assert(!CanDivByZero<0>);
+static_assert(!CanDivByZero<0L>);
+static_assert(!CanDivByZero<0LL>);
+#ifndef _LIBCPP_HAS_NO_INT128
+static_assert(!CanDivByZero<static_cast<__int128_t>(0)>);
+#endif
+static_assert(!CanDivByZero<static_cast<unsigned char>(0)>);
+static_assert(!CanDivByZero<static_cast<unsigned short int>(0)>);
+static_assert(!CanDivByZero<0U>);
+static_assert(!CanDivByZero<0UL>);
+static_assert(!CanDivByZero<0ULL>);
+#ifndef _LIBCPP_HAS_NO_INT128
+static_assert(!CanDivByZero<static_cast<__uint128_t>(0)>);
+#endif
+
+// static_assert(CanDivByZero<static_cast<signed char>(0)>);
+// static_assert(CanDivByZero<static_cast<short int>(0)>);
+// static_assert(CanDivByZero<0>);
+// static_assert(CanDivByZero<0L>);
+// static_assert(CanDivByZero<0LL>);
+// #ifndef _LIBCPP_HAS_NO_INT128
+// static_assert(CanDivByZero<static_cast<__int128_t>(0)>);
+// #endif
+// static_assert(CanDivByZero<static_cast<unsigned char>(0)>);
+// static_assert(CanDivByZero<static_cast<unsigned short int>(0)>);
+// static_assert(CanDivByZero<0U>);
+// static_assert(CanDivByZero<0UL>);
+// static_assert(CanDivByZero<0ULL>);
+// #ifndef _LIBCPP_HAS_NO_INT128
+// static_assert(CanDivByZero<static_cast<__uint128_t>(0)>);
+// #endif
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
index c7992bc72e0299..232f8b826520c9 100644
--- 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
@@ -18,8 +18,6 @@
#include <limits>
#include <numeric>
-#include "check_constexpr.h"
-
template <typename IntegerT>
constexpr bool test_signed() {
constexpr auto minVal = std::numeric_limits<IntegerT>::min();
@@ -75,12 +73,6 @@ constexpr bool test_unsigned() {
return true;
}
-template <typename IntegerT>
-void test_constexpr() {
- TEST_EXPRESSION_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{84}));
- TEST_EXPRESSION_NOT_CONSTEXPR(std::div_sat(IntegerT{90}, IntegerT{0}));
-}
-
constexpr bool test() {
// Signed
test_signed<signed char>();
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
deleted file mode 100644
index 5db6efd4ce397e..00000000000000
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.verify.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 div_sat(T x, T y) noexcept; // freestanding
-
-#include <cstdint>
-#include <numeric>
-
-#include "test_macros.h"
-
-template <typename IntegerT>
-constexpr void test_constraint() {
- // expected-error@*:* 8-9 {{no matching function for call to 'div_sat'}}
- [[maybe_unused]] auto sum = std::div_sat(IntegerT{}, IntegerT{});
-}
-
-constexpr bool test() {
- test_constraint<bool>();
- test_constraint<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
- test_constraint<wchar_t>();
-#endif
- test_constraint<char8_t>();
- test_constraint<char16_t>();
- test_constraint<char32_t>();
- test_constraint<float>();
- test_constraint<double>();
- test_constraint<long double>();
-
- return true;
-}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
new file mode 100644
index 00000000000000..20105daf6b0719
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.compile.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mul_sat(T x, T y) noexcept; // freestanding
+
+#include <concepts>
+#include <numeric>
+#include <limits>
+
+template <typename T, typename U>
+concept CanDo = requires(T x, U y) {
+ { std::mul_sat(x, y) } -> std::same_as<T>;
+};
+
+template <typename T, typename U>
+constexpr void test_constraint_success() {
+ static_assert(CanDo<T, T>);
+ static_assert(!CanDo<U, T>);
+ static_assert(!CanDo<T, U>);
+}
+
+template <typename T>
+constexpr void test_constraint_fail() {
+ using I = int;
+ static_assert(!CanDo<T, T>);
+ static_assert(!CanDo<I, T>);
+ static_assert(!CanDo<T, I>);
+}
+
+constexpr void test() {
+ // Contraint success - Signed
+ using SI = long long int;
+ test_constraint_success<signed char, SI>();
+ test_constraint_success<short int, SI>();
+ test_constraint_success<signed char, SI>();
+ test_constraint_success<short int, SI>();
+ test_constraint_success<int, SI>();
+ test_constraint_success<long int, SI>();
+ test_constraint_success<long long int, int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_constraint_success<__int128_t, SI>();
+#endif
+ // Contraint success - Unsigned
+ using UI = unsigned long long int;
+ test_constraint_success<unsigned char, UI>();
+ test_constraint_success<unsigned short int, UI>();
+ test_constraint_success<unsigned int, UI>();
+ test_constraint_success<unsigned long int, UI>();
+ test_constraint_success<unsigned long long int, unsigned int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_constraint_success<__uint128_t, UI>();
+#endif
+
+ // Contraint failure
+ test_constraint_fail<bool>();
+ test_constraint_fail<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_constraint_fail<wchar_t>();
+#endif
+ test_constraint_fail<char8_t>();
+ test_constraint_fail<char16_t>();
+ test_constraint_fail<char32_t>();
+ test_constraint_fail<float>();
+ test_constraint_fail<double>();
+ test_constraint_fail<long double>();
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
deleted file mode 100644
index fcc24c09b7b9b4..00000000000000
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.verify.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 mul_sat(T x, T y) noexcept; // freestanding
-
-#include <cstdint>
-#include <numeric>
-
-#include "test_macros.h"
-
-template <typename IntegerT>
-constexpr void test_constraint() {
- // expected-error@*:* 8-9 {{no matching function for call to 'mul_sat'}}
- [[maybe_unused]] auto sum = std::mul_sat(IntegerT{}, IntegerT{});
-}
-
-constexpr bool test() {
- test_constraint<bool>();
- test_constraint<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
- test_constraint<wchar_t>();
-#endif
- test_constraint<char8_t>();
- test_constraint<char16_t>();
- test_constraint<char32_t>();
- test_constraint<float>();
- test_constraint<double>();
- test_constraint<long double>();
-
- return true;
-}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
new file mode 100644
index 00000000000000..a1f53e0fd2cac0
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.compile.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 R, class T>
+// constexpr R saturate_cast(T x) noexcept; // freestanding
+
+#include <concepts>
+#include <numeric>
+#include <limits>
+
+template <typename R, typename T>
+concept CanDo = requires(T x) {
+ { std::saturate_cast<R>(x) } -> std::same_as<R>;
+};
+
+template <typename R, typename T>
+constexpr void test_constraint_success() {
+ static_assert(CanDo<R, T>);
+ static_assert(CanDo<T, T>);
+}
+
+template <typename T>
+constexpr void test_constraint_fail() {
+ using I = int;
+ using R = T;
+ static_assert(!CanDo<R, T>);
+ static_assert(!CanDo<T, R>);
+ static_assert(!CanDo<I, T>);
+ static_assert(!CanDo<T, I>);
+}
+
+constexpr void test() {
+ // Contraint success - Signed
+ using SI = long long int;
+ test_constraint_success<SI, signed char>();
+ test_constraint_success<SI, short int>();
+ test_constraint_success<SI, signed char>();
+ test_constraint_success<SI, short int>();
+ test_constraint_success<SI, int>();
+ test_constraint_success<SI, long int>();
+ test_constraint_success<int, long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_constraint_success<__int128_t, SI>();
+#endif
+ // Contraint success - Unsigned
+ using UI = unsigned long long int;
+ test_constraint_success<UI, unsigned char>();
+ test_constraint_success<UI, unsigned short int>();
+ test_constraint_success<UI, unsigned int>();
+ test_constraint_success<UI, unsigned long int>();
+ test_constraint_success<unsigned int, unsigned long long int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_constraint_success<UI, __uint128_t>();
+#endif
+
+ // Contraint failure
+ test_constraint_fail<bool>();
+ test_constraint_fail<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_constraint_fail<wchar_t>();
+#endif
+ test_constraint_fail<char8_t>();
+ test_constraint_fail<char16_t>();
+ test_constraint_fail<char32_t>();
+ test_constraint_fail<float>();
+ test_constraint_fail<double>();
+ test_constraint_fail<long double>();
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
deleted file mode 100644
index 379628b5664841..00000000000000
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.verify.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 R, class T>
-// constexpr R saturate_cast(T x) noexcept; // freestanding
-
-#include <cstdint>
-#include <numeric>
-
-#include "test_macros.h"
-
-template <typename ResultIntegerT, typename IntegerT>
-constexpr void test_constraint() {
- // expected-error@*:* 16-18 {{no matching function for call to 'saturate_cast'}}
- [[maybe_unused]] auto sum = std::saturate_cast<ResultIntegerT>(IntegerT{});
-}
-
-constexpr bool test() {
- test_constraint<bool, int>();
- test_constraint<char, int>();
- test_constraint<int, bool>();
- test_constraint<int, char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
- test_constraint<wchar_t, int>();
- test_constraint<int, wchar_t>();
-#endif
- test_constraint<char8_t, int>();
- test_constraint<char16_t, int>();
- test_constraint<char32_t, int>();
- test_constraint<float, int>();
- test_constraint<double, int>();
- test_constraint<long double, int>();
- test_constraint<int, char8_t>();
- test_constraint<int, char16_t>();
- test_constraint<int, char32_t>();
- test_constraint<int, float>();
- test_constraint<int, double>();
- test_constraint<int, long double>();
-
- return true;
-}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
new file mode 100644
index 00000000000000..cd27770d9a77de
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.compile.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 sub_sat(T x, T y) noexcept; // freestanding
+
+#include <concepts>
+#include <numeric>
+#include <limits>
+
+template <typename T, typename U>
+concept CanDo = requires(T x, U y) {
+ { std::sub_sat(x, y) } -> std::same_as<T>;
+};
+
+template <typename T, typename U>
+constexpr void test_constraint_success() {
+ static_assert(CanDo<T, T>);
+ static_assert(!CanDo<U, T>);
+ static_assert(!CanDo<T, U>);
+}
+
+template <typename T>
+constexpr void test_constraint_fail() {
+ using I = int;
+ static_assert(!CanDo<T, T>);
+ static_assert(!CanDo<I, T>);
+ static_assert(!CanDo<T, I>);
+}
+
+constexpr void test() {
+ // Contraint success - Signed
+ using SI = long long int;
+ test_constraint_success<signed char, SI>();
+ test_constraint_success<short int, SI>();
+ test_constraint_success<signed char, SI>();
+ test_constraint_success<short int, SI>();
+ test_constraint_success<int, SI>();
+ test_constraint_success<long int, SI>();
+ test_constraint_success<long long int, int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_constraint_success<__int128_t, SI>();
+#endif
+ // Contraint success - Unsigned
+ using UI = unsigned long long int;
+ test_constraint_success<unsigned char, UI>();
+ test_constraint_success<unsigned short int, UI>();
+ test_constraint_success<unsigned int, UI>();
+ test_constraint_success<unsigned long int, UI>();
+ test_constraint_success<unsigned long long int, unsigned int>();
+#ifndef _LIBCPP_HAS_NO_INT128
+ test_constraint_success<__uint128_t, UI>();
+#endif
+
+ // Contraint failure
+ test_constraint_fail<bool>();
+ test_constraint_fail<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_constraint_fail<wchar_t>();
+#endif
+ test_constraint_fail<char8_t>();
+ test_constraint_fail<char16_t>();
+ test_constraint_fail<char32_t>();
+ test_constraint_fail<float>();
+ test_constraint_fail<double>();
+ test_constraint_fail<long double>();
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
deleted file mode 100644
index 1ca5ca329b6c9a..00000000000000
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.verify.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 sub_sat(T x, T y) noexcept; // freestanding
-
-#include <cstdint>
-#include <numeric>
-
-#include "test_macros.h"
-
-template <typename IntegerT>
-constexpr void test_constraint() {
- // expected-error@*:* 8-9 {{no matching function for call to 'sub_sat'}}
- [[maybe_unused]] auto sum = std::sub_sat(IntegerT{}, IntegerT{});
-}
-
-constexpr bool test() {
- test_constraint<bool>();
- test_constraint<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
- test_constraint<wchar_t>();
-#endif
- test_constraint<char8_t>();
- test_constraint<char16_t>();
- test_constraint<char32_t>();
- test_constraint<float>();
- test_constraint<double>();
- test_constraint<long double>();
-
- return true;
-}
diff --git a/libcxx/test/support/check_constexpr.h b/libcxx/test/support/check_constexpr.h
deleted file mode 100644
index 54a6c6caf7c945..00000000000000
--- a/libcxx/test/support/check_constexpr.h
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 TEST_SUPPORT_CHECK_CONSTEXPR_H
-#define TEST_SUPPORT_CHECK_CONSTEXPR_H
-
-#include "test_macros.h"
-
-#if TEST_STD_VER < 11
-# error "C++11 or greater is required to use this header"
-#endif
-
-#define TEST_EXPRESSION_CONSTEXPR(expr) static_assert(__builtin_constant_p(expr))
-#define TEST_EXPRESSION_NOT_CONSTEXPR(expr) static_assert(!__builtin_constant_p(expr));
-
-#endif // TEST_SUPPORT_CHECK_CONSTEXPR_H
>From b66f03446b98c0656dd561219408b119423715ad Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Tue, 16 Jan 2024 22:20:48 +0200
Subject: [PATCH 17/20] WIP: Temporary static tests
---
.../include/__numeric/saturation_arithmetic.h | 7 +-
.../numeric.ops.sat/add_sat.pass.cpp | 71 ++++++++++++++++
.../numeric.ops.sat/div_sat.compile.pass.cpp | 19 +----
.../numeric.ops.sat/div_sat.pass.cpp | 42 ++++++++++
.../numeric.ops.sat/mul_sat.pass.cpp | 31 +++++++
.../numeric.ops.sat/saturate_cast.pass.cpp | 27 ++++++
.../numeric.ops.sat/sub_sat.pass.cpp | 83 +++++++++++++++++++
7 files changed, 260 insertions(+), 20 deletions(-)
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 75dd540a84e689..17efdd4b18c3e0 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -107,8 +107,11 @@ _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();
- // x >= std::numeric_limits<_Rp>::max()
- return std::numeric_limits<_Rp>::max();
+ // // x >= std::numeric_limits<_Rp>::max()
+ // return std::numeric_limits<_Rp>::max();
+ if (std::cmp_greater_equal(__x, std::numeric_limits<_Rp>::max()))
+ return std::numeric_limits<_Rp>::max();
+ return static_cast<_Rp>(__x);
}
}
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
index 1fdf647ff606e4..dc0485b1c75f73 100644
--- 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
@@ -222,3 +222,74 @@ int main(int, char**) {
return 0;
}
+
+
+#include <numeric>
+#include <limits>
+
+template<typename T, typename U>
+concept can_add_sat
+ = requires(T t, U u) { { std::add_sat(t, u) } -> std::same_as<T>; };
+
+static_assert( can_add_sat<int, int> );
+static_assert( not can_add_sat<int, short> );
+static_assert( not can_add_sat<unsigned, int> );
+static_assert( noexcept(std::add_sat(0, 0)) );
+
+using std::add_sat;
+
+// Signed type
+static_assert(add_sat(0, 0) == 0);
+static_assert(add_sat(1, 1) == 2);
+static_assert(add_sat(-1, -1) == -2);
+static_assert(add_sat(-1, 1) == 0);
+constexpr auto max = std::numeric_limits<int>::max();
+constexpr auto min = std::numeric_limits<int>::min();
+static_assert(add_sat(max, 1) == max);
+static_assert(add_sat(1, max) == max);
+static_assert(add_sat(max, max) == max);
+static_assert(add_sat(min, -1) == min);
+static_assert(add_sat(-1, min) == min);
+static_assert(add_sat(min, min) == min);
+static_assert(add_sat(max, min) == -1);
+static_assert(add_sat(min, max) == -1);
+
+// Wider signed type than the args
+static_assert(add_sat<long long>(max, max) == (long long)max * 2);
+static_assert(add_sat<long long>(min, min) == (long long)min * 2);
+
+// Signed type that undergoes integer promotion
+constexpr auto shrt_max = std::numeric_limits<short>::max();
+constexpr auto shrt_min = std::numeric_limits<short>::min();
+static_assert(add_sat<short>(0, 0) == 0);
+static_assert(add_sat<short>(1, 1) == 2);
+static_assert(add_sat<short>(shrt_max, shrt_max) == shrt_max);
+static_assert(add_sat<short>(shrt_max, 1) == shrt_max);
+static_assert(add_sat<short>(1, shrt_max) == shrt_max);
+static_assert(add_sat<short>(shrt_min, (short)-1) == shrt_min);
+static_assert(add_sat<short>((short)-1, shrt_min) == shrt_min);
+static_assert(add_sat<short>(shrt_min, (short)1) == -shrt_max);
+static_assert(add_sat<short>((short)1, shrt_min) == -shrt_max);
+
+// Unsigned type
+static_assert(add_sat(0u, 0u) == 0u);
+static_assert(add_sat(1u, 1u) == 2u);
+constexpr auto umax = std::numeric_limits<unsigned>::max();
+static_assert(add_sat(umax, 1u) == umax);
+static_assert(add_sat(1u, umax) == umax);
+static_assert(add_sat(umax, umax) == umax);
+static_assert(add_sat(0u, umax) == umax);
+static_assert(add_sat(umax, 0u) == umax);
+static_assert(add_sat(0u, 1u) == 1u);
+static_assert(add_sat(1u, 0u) == 1u);
+
+// Wider unsigned type than the args
+static_assert(add_sat<unsigned long long>(umax, umax) == (long long)umax * 2);
+
+// Unsigned type that undergoes integer promotion
+constexpr auto ushrt_max = std::numeric_limits<unsigned short>::max();
+static_assert(add_sat<unsigned short>(0, 0) == 0);
+static_assert(add_sat<unsigned short>(1, 1) == 2);
+static_assert(add_sat<unsigned short>(ushrt_max, ushrt_max) == ushrt_max);
+static_assert(add_sat<unsigned short>(ushrt_max, 1) == ushrt_max);
+static_assert(add_sat<unsigned short>(1, ushrt_max) == ushrt_max);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
index a91c2d6b5d6fdc..ffa30d9e3d5cfd 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/div_sat.compile.pass.cpp
@@ -78,7 +78,7 @@ constexpr void test() {
test_constraint_fail<long double>();
}
-// Check precondition: not constexpr when `div_sat` by zero
+// A function call expression that violates the precondition in the Preconditions: element is not a core constant expression (7.7 [expr.const]).
template <auto N>
using QuotT = std::integral_constant<decltype(N), std::div_sat(N, N)>;
@@ -105,20 +105,3 @@ static_assert(!CanDivByZero<0ULL>);
#ifndef _LIBCPP_HAS_NO_INT128
static_assert(!CanDivByZero<static_cast<__uint128_t>(0)>);
#endif
-
-// static_assert(CanDivByZero<static_cast<signed char>(0)>);
-// static_assert(CanDivByZero<static_cast<short int>(0)>);
-// static_assert(CanDivByZero<0>);
-// static_assert(CanDivByZero<0L>);
-// static_assert(CanDivByZero<0LL>);
-// #ifndef _LIBCPP_HAS_NO_INT128
-// static_assert(CanDivByZero<static_cast<__int128_t>(0)>);
-// #endif
-// static_assert(CanDivByZero<static_cast<unsigned char>(0)>);
-// static_assert(CanDivByZero<static_cast<unsigned short int>(0)>);
-// static_assert(CanDivByZero<0U>);
-// static_assert(CanDivByZero<0UL>);
-// static_assert(CanDivByZero<0ULL>);
-// #ifndef _LIBCPP_HAS_NO_INT128
-// static_assert(CanDivByZero<static_cast<__uint128_t>(0)>);
-// #endif
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
index 232f8b826520c9..9e457f56e9d56c 100644
--- 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
@@ -120,3 +120,45 @@ int main(int, char**) {
return 0;
}
+
+#include <numeric>
+#include <climits>
+
+template<typename T, typename U>
+concept can_div_sat
+ = requires(T t, U u) { { std::div_sat(t, u) } -> std::same_as<T>; };
+
+static_assert( can_div_sat<int, int> );
+static_assert( not can_div_sat<int, short> );
+static_assert( not can_div_sat<unsigned, int> );
+static_assert( noexcept(std::div_sat(0, 1)) );
+
+using std::div_sat;
+
+static_assert(std::div_sat(0, 1) == 0);
+static_assert(std::div_sat(0, -1) == 0);
+static_assert(std::div_sat(1, -1) == -1);
+static_assert(std::div_sat(10, -2) == -5);
+static_assert(std::div_sat(-10, -2) == 5);
+static_assert(std::div_sat(INT_MAX, 1) == INT_MAX);
+static_assert(std::div_sat(INT_MIN, 1) == INT_MIN);
+static_assert(std::div_sat(INT_MIN + 1, -1) == INT_MAX);
+static_assert(std::div_sat(0u, 1u) == 0u);
+static_assert(std::div_sat(UINT_MAX, 1u) == UINT_MAX);
+static_assert(std::div_sat(INT_MIN, -1) == INT_MAX);
+static_assert(std::div_sat((short)SHRT_MIN, (short)-1) == SHRT_MAX);
+static_assert(std::div_sat(LONG_MIN, -1L) == LONG_MAX);
+static_assert(std::div_sat(LLONG_MIN, -1LL) == LLONG_MAX);
+
+template<auto N>
+std::integral_constant<decltype(N), std::div_sat(N, N-N)>
+div_sat_by_zero();
+
+template<auto N>
+concept can_div_sat_by_zero = requires { div_sat_by_zero<N>(); };
+
+static_assert( not can_div_sat_by_zero<0> );
+static_assert( not can_div_sat_by_zero<1> );
+static_assert( not can_div_sat_by_zero<1u> );
+static_assert( not can_div_sat_by_zero<-1L> );
+static_assert( not can_div_sat_by_zero<short(99)> );
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 1f4d0ab518bbea..64721c6ef97e8e 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -109,3 +109,34 @@ int main(int, char**) {
return 0;
}
+
+#include <numeric>
+#include <climits>
+
+template<typename T, typename U>
+concept can_mul_sat
+ = requires(T t, U u) { { std::mul_sat(t, u) } -> std::same_as<T>; };
+
+static_assert( can_mul_sat<int, int> );
+static_assert( not can_mul_sat<int, short> );
+static_assert( not can_mul_sat<unsigned, int> );
+static_assert( noexcept(std::mul_sat(0, 0)) );
+
+using std::mul_sat;
+
+static_assert(mul_sat(1, 1) == 1);
+static_assert(mul_sat(10, 11) == 110);
+static_assert(mul_sat(INT_MAX / 2, 3) == INT_MAX);
+static_assert(mul_sat(INT_MAX / 2, -3) == INT_MIN);
+static_assert(mul_sat(INT_MAX / -2, 3) == INT_MIN);
+static_assert(mul_sat(INT_MIN / 2, -3) == INT_MAX);
+static_assert(mul_sat(INT_MIN, -1) == INT_MAX);
+static_assert(mul_sat(INT_MAX, -1) == INT_MIN + 1);
+static_assert(mul_sat(INT_MAX, INT_MAX) == INT_MAX);
+static_assert(mul_sat(INT_MAX, -INT_MAX) == INT_MIN);
+static_assert(mul_sat(UINT_MAX, UINT_MAX) == UINT_MAX);
+static_assert(mul_sat(UINT_MAX, 0u) == 0);
+static_assert(mul_sat(0u, UINT_MAX) == 0);
+static_assert(mul_sat((short)SHRT_MAX, (short)2) == SHRT_MAX);
+static_assert(mul_sat((short)SHRT_MAX, (short)SHRT_MIN) == SHRT_MIN);
+static_assert(mul_sat<long long>(SHRT_MAX, 2) == 2L * SHRT_MAX);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index f7c1cb4dfc09d2..bf6aa23f20279f 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -125,9 +125,36 @@ constexpr bool test() {
return true;
}
+#include <print>
+
int main(int, char**) {
assert(test());
static_assert(test());
+ // std::println(stderr, "-----> {}", std::saturate_cast<signed short>(999));
+ // std::println(stderr, "-----> {}", std::saturate_cast<unsigned short>(999));
+ // assert(false);
+
return 0;
}
+
+#include <numeric>
+#include <climits>
+
+#if CHAR_BIT == 8
+static_assert(std::saturate_cast<unsigned char>(999) == 255);
+static_assert(std::saturate_cast<signed char>(999) == 127);
+#endif
+static_assert(std::saturate_cast<unsigned short>(999) == 999);
+static_assert(std::saturate_cast<signed short>(999) == 999);
+static_assert(std::saturate_cast<short>(INT_MAX) == SHRT_MAX);
+static_assert(std::saturate_cast<short>(UINT_MAX) == SHRT_MAX);
+static_assert(std::saturate_cast<short>(UINT_MAX) == SHRT_MAX);
+static_assert(std::saturate_cast<unsigned short>(UINT_MAX) == USHRT_MAX);
+static_assert(std::saturate_cast<int>(UINT_MAX) == INT_MAX);
+static_assert(std::saturate_cast<int>(INT_MAX) == INT_MAX);
+static_assert(std::saturate_cast<unsigned>(-1) == 0);
+static_assert(std::saturate_cast<unsigned>(INT_MIN) == 0);
+static_assert(std::saturate_cast<unsigned>(UINT_MAX) == UINT_MAX);
+static_assert(std::saturate_cast<unsigned>(LLONG_MAX) == UINT_MAX);
+static_assert(std::saturate_cast<unsigned>(ULLONG_MAX) == UINT_MAX);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index 2c5fcf6ec0f699..3b3499bde792f7 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -121,3 +121,86 @@ int main(int, char**) {
return 0;
}
+
+#include <numeric>
+#include <limits>
+
+template<typename T, typename U>
+concept can_sub_sat
+ = requires(T t, U u) { { std::sub_sat(t, u) } -> std::same_as<T>; };
+
+static_assert( can_sub_sat<int, int> );
+static_assert( not can_sub_sat<int, short> );
+static_assert( not can_sub_sat<unsigned, int> );
+static_assert( noexcept(std::sub_sat(0, 0)) );
+
+using std::sub_sat;
+
+// Signed type
+static_assert(sub_sat(0, 0) == 0);
+static_assert(sub_sat(1, 1) == 0);
+static_assert(sub_sat(-1, -1) == 0);
+static_assert(sub_sat(-1, 1) == -2);
+constexpr auto max = std::numeric_limits<int>::max();
+constexpr auto min = std::numeric_limits<int>::min();
+static_assert(sub_sat(max, 1) == max - 1);
+static_assert(sub_sat(1, max) == 1 - max);
+static_assert(sub_sat(max, max) == 0);
+static_assert(sub_sat(min, 1) == min);
+static_assert(sub_sat(min, 123) == min);
+static_assert(sub_sat(0, max) == min + 1);
+static_assert(sub_sat(-1, max) == min);
+static_assert(sub_sat(-2, max) == min);
+static_assert(sub_sat(-2, min) == max - 1);
+static_assert(sub_sat(-1, min) == max);
+static_assert(sub_sat(0, min) == max);
+static_assert(sub_sat(1, min) == max);
+static_assert(sub_sat(min, -1) == min + 1);
+static_assert(sub_sat(min, min) == 0);
+static_assert(sub_sat(max, min) == max);
+static_assert(sub_sat(min, max) == min);
+
+// Wider signed type than the args
+static_assert(sub_sat<long long>(max, min) == (long long)max * 2 + 1);
+static_assert(sub_sat<long long>(min, max) == (long long)min * 2 + 1);
+
+// Signed type that undergoes integer promotion
+constexpr auto shrt_max = std::numeric_limits<short>::max();
+constexpr auto shrt_min = std::numeric_limits<short>::min();
+static_assert(sub_sat<short>(0, 0) == 0);
+static_assert(sub_sat<short>(1, 1) == 0);
+static_assert(sub_sat<short>(3, 1) == 2);
+static_assert(sub_sat<short>(shrt_max, shrt_max) == 0);
+static_assert(sub_sat<short>(shrt_max, 1) == shrt_max - 1);
+static_assert(sub_sat<short>(1, shrt_max) == shrt_min + 2);
+static_assert(sub_sat<short>(shrt_max, shrt_min) == shrt_max);
+static_assert(sub_sat<short>(0, shrt_min) == shrt_max);
+static_assert(sub_sat<short>(shrt_min, (short)1) == shrt_min);
+static_assert(sub_sat<short>(shrt_min, (short)-1) == shrt_min + 1);
+static_assert(sub_sat<short>((short)-1, shrt_min) == shrt_max);
+static_assert(sub_sat<short>((short)1, shrt_min) == shrt_max);
+
+// Unsigned type
+static_assert(sub_sat(0u, 0u) == 0u);
+static_assert(sub_sat(1u, 1u) == 0u);
+static_assert(sub_sat(-1u, -1u) == 0u);
+static_assert(sub_sat(-1u, 1u) == -2u);
+constexpr auto umax = std::numeric_limits<unsigned>::max();
+static_assert(sub_sat(0u, 1u) == 0u);
+static_assert(sub_sat(umax, umax) == 0u);
+static_assert(sub_sat(umax, 0u) == umax);
+static_assert(sub_sat(0u, umax) == 0u);
+static_assert(sub_sat(umax, 1u) == umax - 1u);
+static_assert(sub_sat(0u, 0u) == 0u);
+
+// Wider unsigned type than the args
+static_assert(sub_sat<unsigned long long>(0u, umax) == 0u);
+
+// Unsigned type that undergoes integer promotion
+constexpr auto ushrt_max = std::numeric_limits<unsigned short>::max();
+static_assert(sub_sat<unsigned short>(0, 0) == 0);
+static_assert(sub_sat<unsigned short>(1, 1) == 0);
+static_assert(sub_sat<unsigned short>(3, 1) == 2);
+static_assert(sub_sat<unsigned short>(ushrt_max, ushrt_max) == 0);
+static_assert(sub_sat<unsigned short>(0, 1) == 0);
+static_assert(sub_sat<unsigned short>(1, ushrt_max) == 0);
>From 248531aa9c21bda39f5482a87e49d4f451050033 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Tue, 16 Jan 2024 22:23:49 +0200
Subject: [PATCH 18/20] WIP: Cleanup
---
.../include/__numeric/saturation_arithmetic.h | 3 +-
.../numeric.ops.sat/add_sat.pass.cpp | 71 ----------------
.../numeric.ops.sat/div_sat.pass.cpp | 42 ----------
.../numeric.ops.sat/mul_sat.pass.cpp | 31 -------
.../numeric.ops.sat/saturate_cast.pass.cpp | 27 ------
.../numeric.ops.sat/sub_sat.pass.cpp | 83 -------------------
6 files changed, 1 insertion(+), 256 deletions(-)
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 17efdd4b18c3e0..53955ab0a6a613 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -107,10 +107,9 @@ _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();
- // // x >= std::numeric_limits<_Rp>::max()
- // return std::numeric_limits<_Rp>::max();
if (std::cmp_greater_equal(__x, std::numeric_limits<_Rp>::max()))
return std::numeric_limits<_Rp>::max();
+ // No overflow
return static_cast<_Rp>(__x);
}
}
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
index dc0485b1c75f73..1fdf647ff606e4 100644
--- 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
@@ -222,74 +222,3 @@ int main(int, char**) {
return 0;
}
-
-
-#include <numeric>
-#include <limits>
-
-template<typename T, typename U>
-concept can_add_sat
- = requires(T t, U u) { { std::add_sat(t, u) } -> std::same_as<T>; };
-
-static_assert( can_add_sat<int, int> );
-static_assert( not can_add_sat<int, short> );
-static_assert( not can_add_sat<unsigned, int> );
-static_assert( noexcept(std::add_sat(0, 0)) );
-
-using std::add_sat;
-
-// Signed type
-static_assert(add_sat(0, 0) == 0);
-static_assert(add_sat(1, 1) == 2);
-static_assert(add_sat(-1, -1) == -2);
-static_assert(add_sat(-1, 1) == 0);
-constexpr auto max = std::numeric_limits<int>::max();
-constexpr auto min = std::numeric_limits<int>::min();
-static_assert(add_sat(max, 1) == max);
-static_assert(add_sat(1, max) == max);
-static_assert(add_sat(max, max) == max);
-static_assert(add_sat(min, -1) == min);
-static_assert(add_sat(-1, min) == min);
-static_assert(add_sat(min, min) == min);
-static_assert(add_sat(max, min) == -1);
-static_assert(add_sat(min, max) == -1);
-
-// Wider signed type than the args
-static_assert(add_sat<long long>(max, max) == (long long)max * 2);
-static_assert(add_sat<long long>(min, min) == (long long)min * 2);
-
-// Signed type that undergoes integer promotion
-constexpr auto shrt_max = std::numeric_limits<short>::max();
-constexpr auto shrt_min = std::numeric_limits<short>::min();
-static_assert(add_sat<short>(0, 0) == 0);
-static_assert(add_sat<short>(1, 1) == 2);
-static_assert(add_sat<short>(shrt_max, shrt_max) == shrt_max);
-static_assert(add_sat<short>(shrt_max, 1) == shrt_max);
-static_assert(add_sat<short>(1, shrt_max) == shrt_max);
-static_assert(add_sat<short>(shrt_min, (short)-1) == shrt_min);
-static_assert(add_sat<short>((short)-1, shrt_min) == shrt_min);
-static_assert(add_sat<short>(shrt_min, (short)1) == -shrt_max);
-static_assert(add_sat<short>((short)1, shrt_min) == -shrt_max);
-
-// Unsigned type
-static_assert(add_sat(0u, 0u) == 0u);
-static_assert(add_sat(1u, 1u) == 2u);
-constexpr auto umax = std::numeric_limits<unsigned>::max();
-static_assert(add_sat(umax, 1u) == umax);
-static_assert(add_sat(1u, umax) == umax);
-static_assert(add_sat(umax, umax) == umax);
-static_assert(add_sat(0u, umax) == umax);
-static_assert(add_sat(umax, 0u) == umax);
-static_assert(add_sat(0u, 1u) == 1u);
-static_assert(add_sat(1u, 0u) == 1u);
-
-// Wider unsigned type than the args
-static_assert(add_sat<unsigned long long>(umax, umax) == (long long)umax * 2);
-
-// Unsigned type that undergoes integer promotion
-constexpr auto ushrt_max = std::numeric_limits<unsigned short>::max();
-static_assert(add_sat<unsigned short>(0, 0) == 0);
-static_assert(add_sat<unsigned short>(1, 1) == 2);
-static_assert(add_sat<unsigned short>(ushrt_max, ushrt_max) == ushrt_max);
-static_assert(add_sat<unsigned short>(ushrt_max, 1) == ushrt_max);
-static_assert(add_sat<unsigned short>(1, ushrt_max) == ushrt_max);
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
index 9e457f56e9d56c..232f8b826520c9 100644
--- 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
@@ -120,45 +120,3 @@ int main(int, char**) {
return 0;
}
-
-#include <numeric>
-#include <climits>
-
-template<typename T, typename U>
-concept can_div_sat
- = requires(T t, U u) { { std::div_sat(t, u) } -> std::same_as<T>; };
-
-static_assert( can_div_sat<int, int> );
-static_assert( not can_div_sat<int, short> );
-static_assert( not can_div_sat<unsigned, int> );
-static_assert( noexcept(std::div_sat(0, 1)) );
-
-using std::div_sat;
-
-static_assert(std::div_sat(0, 1) == 0);
-static_assert(std::div_sat(0, -1) == 0);
-static_assert(std::div_sat(1, -1) == -1);
-static_assert(std::div_sat(10, -2) == -5);
-static_assert(std::div_sat(-10, -2) == 5);
-static_assert(std::div_sat(INT_MAX, 1) == INT_MAX);
-static_assert(std::div_sat(INT_MIN, 1) == INT_MIN);
-static_assert(std::div_sat(INT_MIN + 1, -1) == INT_MAX);
-static_assert(std::div_sat(0u, 1u) == 0u);
-static_assert(std::div_sat(UINT_MAX, 1u) == UINT_MAX);
-static_assert(std::div_sat(INT_MIN, -1) == INT_MAX);
-static_assert(std::div_sat((short)SHRT_MIN, (short)-1) == SHRT_MAX);
-static_assert(std::div_sat(LONG_MIN, -1L) == LONG_MAX);
-static_assert(std::div_sat(LLONG_MIN, -1LL) == LLONG_MAX);
-
-template<auto N>
-std::integral_constant<decltype(N), std::div_sat(N, N-N)>
-div_sat_by_zero();
-
-template<auto N>
-concept can_div_sat_by_zero = requires { div_sat_by_zero<N>(); };
-
-static_assert( not can_div_sat_by_zero<0> );
-static_assert( not can_div_sat_by_zero<1> );
-static_assert( not can_div_sat_by_zero<1u> );
-static_assert( not can_div_sat_by_zero<-1L> );
-static_assert( not can_div_sat_by_zero<short(99)> );
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
index 64721c6ef97e8e..1f4d0ab518bbea 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/mul_sat.pass.cpp
@@ -109,34 +109,3 @@ int main(int, char**) {
return 0;
}
-
-#include <numeric>
-#include <climits>
-
-template<typename T, typename U>
-concept can_mul_sat
- = requires(T t, U u) { { std::mul_sat(t, u) } -> std::same_as<T>; };
-
-static_assert( can_mul_sat<int, int> );
-static_assert( not can_mul_sat<int, short> );
-static_assert( not can_mul_sat<unsigned, int> );
-static_assert( noexcept(std::mul_sat(0, 0)) );
-
-using std::mul_sat;
-
-static_assert(mul_sat(1, 1) == 1);
-static_assert(mul_sat(10, 11) == 110);
-static_assert(mul_sat(INT_MAX / 2, 3) == INT_MAX);
-static_assert(mul_sat(INT_MAX / 2, -3) == INT_MIN);
-static_assert(mul_sat(INT_MAX / -2, 3) == INT_MIN);
-static_assert(mul_sat(INT_MIN / 2, -3) == INT_MAX);
-static_assert(mul_sat(INT_MIN, -1) == INT_MAX);
-static_assert(mul_sat(INT_MAX, -1) == INT_MIN + 1);
-static_assert(mul_sat(INT_MAX, INT_MAX) == INT_MAX);
-static_assert(mul_sat(INT_MAX, -INT_MAX) == INT_MIN);
-static_assert(mul_sat(UINT_MAX, UINT_MAX) == UINT_MAX);
-static_assert(mul_sat(UINT_MAX, 0u) == 0);
-static_assert(mul_sat(0u, UINT_MAX) == 0);
-static_assert(mul_sat((short)SHRT_MAX, (short)2) == SHRT_MAX);
-static_assert(mul_sat((short)SHRT_MAX, (short)SHRT_MIN) == SHRT_MIN);
-static_assert(mul_sat<long long>(SHRT_MAX, 2) == 2L * SHRT_MAX);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index bf6aa23f20279f..f7c1cb4dfc09d2 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -125,36 +125,9 @@ constexpr bool test() {
return true;
}
-#include <print>
-
int main(int, char**) {
assert(test());
static_assert(test());
- // std::println(stderr, "-----> {}", std::saturate_cast<signed short>(999));
- // std::println(stderr, "-----> {}", std::saturate_cast<unsigned short>(999));
- // assert(false);
-
return 0;
}
-
-#include <numeric>
-#include <climits>
-
-#if CHAR_BIT == 8
-static_assert(std::saturate_cast<unsigned char>(999) == 255);
-static_assert(std::saturate_cast<signed char>(999) == 127);
-#endif
-static_assert(std::saturate_cast<unsigned short>(999) == 999);
-static_assert(std::saturate_cast<signed short>(999) == 999);
-static_assert(std::saturate_cast<short>(INT_MAX) == SHRT_MAX);
-static_assert(std::saturate_cast<short>(UINT_MAX) == SHRT_MAX);
-static_assert(std::saturate_cast<short>(UINT_MAX) == SHRT_MAX);
-static_assert(std::saturate_cast<unsigned short>(UINT_MAX) == USHRT_MAX);
-static_assert(std::saturate_cast<int>(UINT_MAX) == INT_MAX);
-static_assert(std::saturate_cast<int>(INT_MAX) == INT_MAX);
-static_assert(std::saturate_cast<unsigned>(-1) == 0);
-static_assert(std::saturate_cast<unsigned>(INT_MIN) == 0);
-static_assert(std::saturate_cast<unsigned>(UINT_MAX) == UINT_MAX);
-static_assert(std::saturate_cast<unsigned>(LLONG_MAX) == UINT_MAX);
-static_assert(std::saturate_cast<unsigned>(ULLONG_MAX) == UINT_MAX);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
index 3b3499bde792f7..2c5fcf6ec0f699 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/sub_sat.pass.cpp
@@ -121,86 +121,3 @@ int main(int, char**) {
return 0;
}
-
-#include <numeric>
-#include <limits>
-
-template<typename T, typename U>
-concept can_sub_sat
- = requires(T t, U u) { { std::sub_sat(t, u) } -> std::same_as<T>; };
-
-static_assert( can_sub_sat<int, int> );
-static_assert( not can_sub_sat<int, short> );
-static_assert( not can_sub_sat<unsigned, int> );
-static_assert( noexcept(std::sub_sat(0, 0)) );
-
-using std::sub_sat;
-
-// Signed type
-static_assert(sub_sat(0, 0) == 0);
-static_assert(sub_sat(1, 1) == 0);
-static_assert(sub_sat(-1, -1) == 0);
-static_assert(sub_sat(-1, 1) == -2);
-constexpr auto max = std::numeric_limits<int>::max();
-constexpr auto min = std::numeric_limits<int>::min();
-static_assert(sub_sat(max, 1) == max - 1);
-static_assert(sub_sat(1, max) == 1 - max);
-static_assert(sub_sat(max, max) == 0);
-static_assert(sub_sat(min, 1) == min);
-static_assert(sub_sat(min, 123) == min);
-static_assert(sub_sat(0, max) == min + 1);
-static_assert(sub_sat(-1, max) == min);
-static_assert(sub_sat(-2, max) == min);
-static_assert(sub_sat(-2, min) == max - 1);
-static_assert(sub_sat(-1, min) == max);
-static_assert(sub_sat(0, min) == max);
-static_assert(sub_sat(1, min) == max);
-static_assert(sub_sat(min, -1) == min + 1);
-static_assert(sub_sat(min, min) == 0);
-static_assert(sub_sat(max, min) == max);
-static_assert(sub_sat(min, max) == min);
-
-// Wider signed type than the args
-static_assert(sub_sat<long long>(max, min) == (long long)max * 2 + 1);
-static_assert(sub_sat<long long>(min, max) == (long long)min * 2 + 1);
-
-// Signed type that undergoes integer promotion
-constexpr auto shrt_max = std::numeric_limits<short>::max();
-constexpr auto shrt_min = std::numeric_limits<short>::min();
-static_assert(sub_sat<short>(0, 0) == 0);
-static_assert(sub_sat<short>(1, 1) == 0);
-static_assert(sub_sat<short>(3, 1) == 2);
-static_assert(sub_sat<short>(shrt_max, shrt_max) == 0);
-static_assert(sub_sat<short>(shrt_max, 1) == shrt_max - 1);
-static_assert(sub_sat<short>(1, shrt_max) == shrt_min + 2);
-static_assert(sub_sat<short>(shrt_max, shrt_min) == shrt_max);
-static_assert(sub_sat<short>(0, shrt_min) == shrt_max);
-static_assert(sub_sat<short>(shrt_min, (short)1) == shrt_min);
-static_assert(sub_sat<short>(shrt_min, (short)-1) == shrt_min + 1);
-static_assert(sub_sat<short>((short)-1, shrt_min) == shrt_max);
-static_assert(sub_sat<short>((short)1, shrt_min) == shrt_max);
-
-// Unsigned type
-static_assert(sub_sat(0u, 0u) == 0u);
-static_assert(sub_sat(1u, 1u) == 0u);
-static_assert(sub_sat(-1u, -1u) == 0u);
-static_assert(sub_sat(-1u, 1u) == -2u);
-constexpr auto umax = std::numeric_limits<unsigned>::max();
-static_assert(sub_sat(0u, 1u) == 0u);
-static_assert(sub_sat(umax, umax) == 0u);
-static_assert(sub_sat(umax, 0u) == umax);
-static_assert(sub_sat(0u, umax) == 0u);
-static_assert(sub_sat(umax, 1u) == umax - 1u);
-static_assert(sub_sat(0u, 0u) == 0u);
-
-// Wider unsigned type than the args
-static_assert(sub_sat<unsigned long long>(0u, umax) == 0u);
-
-// Unsigned type that undergoes integer promotion
-constexpr auto ushrt_max = std::numeric_limits<unsigned short>::max();
-static_assert(sub_sat<unsigned short>(0, 0) == 0);
-static_assert(sub_sat<unsigned short>(1, 1) == 0);
-static_assert(sub_sat<unsigned short>(3, 1) == 2);
-static_assert(sub_sat<unsigned short>(ushrt_max, ushrt_max) == 0);
-static_assert(sub_sat<unsigned short>(0, 1) == 0);
-static_assert(sub_sat<unsigned short>(1, ushrt_max) == 0);
>From 14fb0cfa423e933294a26dfd4efa61cd66e95dcf Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Wed, 17 Jan 2024 11:50:28 +0200
Subject: [PATCH 19/20] Updated tests: `add_sat`
---
.../include/__numeric/saturation_arithmetic.h | 23 ++++---
.../numeric.ops.sat/add_sat.pass.cpp | 60 ++++++++++++++++++-
.../numeric.ops.sat/saturate_cast.pass.cpp | 34 +++++++++++
3 files changed, 102 insertions(+), 15 deletions(-)
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 53955ab0a6a613..ef8ad824055d46 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -99,19 +99,16 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
template <__libcpp_integer _Rp, __libcpp_integer _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
- // Saturation is impossible.
- if constexpr (std::cmp_less_equal(std::numeric_limits<_Rp>::min(), std::numeric_limits<_Tp>::min()) &&
- std::cmp_greater_equal(std::numeric_limits<_Rp>::max(), std::numeric_limits<_Tp>::max()))
- return static_cast<_Rp>(__x);
- else {
- // 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);
- }
+ // Saturation is impossible edge case when min _Rp < min _Tp and max _Rp > _Tp is expected to be optimized out by the
+ // compiler.
+
+ // Handle overflow
+ if (std::cmp_less(__x, std::numeric_limits<_Rp>::min()))
+ return std::numeric_limits<_Rp>::min();
+ if (std::cmp_greater(__x, std::numeric_limits<_Rp>::max()))
+ return std::numeric_limits<_Rp>::max();
+ // No overflow
+ return static_cast<_Rp>(__x);
}
#endif // _LIBCPP_STD_VER >= 26
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
index 1fdf647ff606e4..55c4e1ada9fb92 100644
--- 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
@@ -105,6 +105,16 @@ constexpr bool test_signed() {
assert(sum == maxVal + IntegerT{-1});
}
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, maxVal);
+ assert(sum == IntegerT{-1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, minVal);
+ assert(sum == IntegerT{-1});
+ }
+
// Saturation - max - both arguments positive
{
std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
@@ -151,9 +161,25 @@ constexpr bool test_unsigned() {
static_assert(noexcept(std::div_sat(minVal, maxVal)));
// No Saturation
+
{
- std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
- assert(sum == IntegerT{55});
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{0});
+ assert(sum == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, IntegerT{1});
+ assert(sum == IntegerT{1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{0});
+ assert(sum == IntegerT{1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, IntegerT{1});
+ assert(sum == IntegerT{2});
}
{
@@ -166,12 +192,42 @@ constexpr bool test_unsigned() {
assert(sum == IntegerT{1});
}
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{0}, minVal);
+ assert(sum == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{1}, minVal);
+ assert(sum == IntegerT{1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(IntegerT{27}, IntegerT{28});
+ assert(sum == IntegerT{55});
+ }
+
{
std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, minVal);
assert(sum == minVal);
}
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(minVal, maxVal);
+ assert(sum == maxVal);
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, minVal);
+ assert(sum == maxVal);
+ }
+
// Saturation - max only
+ {
+ std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{1});
+ assert(sum == maxVal);
+ }
+
{
std::same_as<IntegerT> decltype(auto) sum = std::add_sat(maxVal, IntegerT{27});
assert(sum == maxVal);
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index f7c1cb4dfc09d2..a2b3b982a6eb9a 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -17,6 +17,16 @@
#include <concepts>
#include <limits>
#include <numeric>
+#include <utility>
+
+// Signed T1 < Singed T2 - overflow
+// Signed T1 == Signed T2 - no overflow
+// Signed T1 > Signed T2 - no overflow
+
+// Unsigned T1 < Unsigned T2 - overflow
+// Unsigned T1 == Unsigned T2 - no overflow
+// Unsigned T1 > Unsigned T2 - no overflow
+
template <typename IntegerResultT, typename IntegerT>
constexpr bool test_signed_notsaturated() {
@@ -26,6 +36,22 @@ constexpr bool test_signed_notsaturated() {
static_assert(noexcept(std::saturate_cast<IntegerResultT>(minVal)));
static_assert(noexcept(std::saturate_cast<IntegerResultT>(maxVal)));
+ assert(std::saturate_cast<IntegerResultT>(IntegerT{-1}) == IntegerT{-1});
+ assert(std::saturate_cast<IntegerResultT>(IntegerT{0}) == IntegerT{0});
+ assert(std::saturate_cast<IntegerResultT>(IntegerT{1}) == IntegerT{1});
+
+ {
+ // Large values
+ constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
+ assert(std::saturate_cast<IntegerResultT>(x) == x);
+ }
+
+ {
+ // Large values
+ constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+ assert(std::saturate_cast<IntegerResultT>(x) == x);
+ }
+
assert(std::saturate_cast<IntegerResultT>(minVal) == minVal);
assert(std::saturate_cast<IntegerResultT>(maxVal) == maxVal);
@@ -43,6 +69,14 @@ constexpr bool test_signed_saturated() {
assert(std::saturate_cast<IntegerResultT>(minVal) == std::numeric_limits<IntegerResultT>::min());
assert(std::saturate_cast<IntegerResultT>(maxVal) == std::numeric_limits<IntegerResultT>::max());
+ if constexpr (std::cmp_less(std::numeric_limits<IntegerResultT>::min(), std::numeric_limits<IntegerT>::min())) {
+ assert(std::saturate_cast<IntegerResultT>(minVal - IntegerT{1}) == std::numeric_limits<IntegerResultT>::min());
+ }
+
+ if constexpr (std::cmp_greater(std::numeric_limits<IntegerResultT>::max(), std::numeric_limits<IntegerT>::max())) {
+ assert(std::saturate_cast<IntegerResultT>(minVal - IntegerT{1}) == std::numeric_limits<IntegerResultT>::min());
+ }
+
return true;
}
>From 179ebaa67ab2fa5dffdd510ffbc02532fedb1b67 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Wed, 17 Jan 2024 12:40:52 +0200
Subject: [PATCH 20/20] Updated tests: `div_sat`
---
.../numeric.ops.sat/add_sat.pass.cpp | 1 +
.../numeric.ops.sat/div_sat.pass.cpp | 196 ++++++++++++++++--
.../numeric.ops.sat/saturate_cast.pass.cpp | 9 -
3 files changed, 176 insertions(+), 30 deletions(-)
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
index 55c4e1ada9fb92..a739061f2672f2 100644
--- 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
@@ -234,6 +234,7 @@ constexpr bool test_unsigned() {
}
{
+ // Large values
constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
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
index 232f8b826520c9..7a21bb3f3a60ee 100644
--- 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
@@ -27,10 +27,125 @@ constexpr bool test_signed() {
// No saturation
{
- std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{3}, IntegerT{4});
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, IntegerT{-1});
+ assert(quot == IntegerT{1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, IntegerT{1});
+ assert(quot == IntegerT{-1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, minVal);
+ assert(quot == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, maxVal);
+ assert(quot == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, IntegerT{-1});
+ assert(quot == IntegerT{-1} * maxVal);
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, IntegerT{-1});
+ assert(quot == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, IntegerT{1});
+ assert(quot == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, minVal);
+ assert(quot == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, IntegerT{-1});
+ assert(quot == IntegerT{-1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, minVal);
+ assert(quot == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, maxVal);
assert(quot == IntegerT{0});
}
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, IntegerT{1});
+ assert(quot == minVal);
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, IntegerT{1});
+ assert(quot == maxVal);
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{27}, IntegerT{28});
+ assert(quot == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{28}, IntegerT{27});
+ assert(quot == IntegerT{1});
+ }
+
+ {
+ // Large values
+ constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
+ constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+ std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+ assert(sum == IntegerT{-1});
+ }
+
+ {
+ // Large values
+ constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{28};
+ constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-27};
+
+ std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+ assert(sum == IntegerT{-1});
+ }
+
+ {
+ // Large values
+ constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-27};
+ constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-28};
+
+ std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+ assert(sum == IntegerT{0});
+ }
+
+ {
+ // Large values
+ constexpr IntegerT x = minVal / IntegerT{2} + IntegerT{-28};
+ constexpr IntegerT y = minVal / IntegerT{2} + IntegerT{-27};
+
+ std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+ assert(sum == IntegerT{1});
+ }
+
+ {
+ // Large values
+ constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+ constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+ std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+ assert(sum == IntegerT{0});
+ }
+
{
std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, minVal);
assert(quot == (maxVal / minVal));
@@ -59,16 +174,64 @@ constexpr bool test_unsigned() {
// No saturation
{
- std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{3}, IntegerT{4});
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, IntegerT{1});
+ assert(quot == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{0}, maxVal);
+ assert(quot == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, IntegerT{1});
+ assert(quot == IntegerT{1});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{1}, maxVal);
assert(quot == IntegerT{0});
}
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{27}, IntegerT{28});
+ assert(quot == IntegerT{0});
+ }
+
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{28}, IntegerT{27});
+ assert(quot == IntegerT{1});
+ }
+
+ {
+ // Large values
+ constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{27};
+ constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{28};
+
+ std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+ assert(sum == IntegerT{0});
+ }
+
+ {
+ // Large values
+ constexpr IntegerT x = maxVal / IntegerT{2} + IntegerT{28};
+ constexpr IntegerT y = maxVal / IntegerT{2} + IntegerT{27};
+
+ std::same_as<IntegerT> decltype(auto) sum = std::div_sat(x, y);
+ assert(sum == IntegerT{1});
+ }
+
{
std::same_as<IntegerT> decltype(auto) quot = std::div_sat(minVal, maxVal);
- assert(quot == (minVal / maxVal));
+ assert(quot == IntegerT{0});
}
- // Unsigned integer devision never overflow
+ {
+ std::same_as<IntegerT> decltype(auto) quot = std::div_sat(maxVal, maxVal);
+ assert(quot == IntegerT{1});
+ }
+
+ // Unsigned integer devision never overflows
return true;
}
@@ -96,27 +259,18 @@ constexpr bool test() {
return true;
}
-bool test_constexpr() {
- // Signed
- test_constexpr<signed char>();
- test_constexpr<short int>();
- test_constexpr<int>();
- test_constexpr<long int>();
- test_constexpr<long long int>();
- // Unsigned
- test_constexpr<unsigned char>();
- test_constexpr<unsigned short int>();
- test_constexpr<unsigned int>();
- test_constexpr<unsigned long int>();
- test_constexpr<unsigned long long int>();
-
- return true;
-}
+// #include <print>
int main(int, char**) {
+ using IntegerT = int;
+ // constexpr auto minVal = std::numeric_limits<IntegerT>::min();
+
+ // std::same_as<IntegerT> decltype(auto) quot = std::div_sat(IntegerT{-1}, minVal);
+
+ // std::println(stderr, "--------> {}", quot);
+ // assert(false);
assert(test());
static_assert(test());
- assert(test_constexpr());
return 0;
}
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
index a2b3b982a6eb9a..8c53e3130c7fd9 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturate_cast.pass.cpp
@@ -19,15 +19,6 @@
#include <numeric>
#include <utility>
-// Signed T1 < Singed T2 - overflow
-// Signed T1 == Signed T2 - no overflow
-// Signed T1 > Signed T2 - no overflow
-
-// Unsigned T1 < Unsigned T2 - overflow
-// Unsigned T1 == Unsigned T2 - no overflow
-// Unsigned T1 > Unsigned T2 - no overflow
-
-
template <typename IntegerResultT, typename IntegerT>
constexpr bool test_signed_notsaturated() {
constexpr auto minVal = std::numeric_limits<IntegerT>::min();
More information about the libc-commits
mailing list