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 <zin...@outlook.com> Date: Wed, 10 Jan 2024 13:46:19 +0200 Subject: [PATCH 1/4] [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 <zin...@outlook.com> Date: Fri, 12 Jan 2024 21:19:39 +0200 Subject: [PATCH 2/4] 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 <zin...@outlook.com> Date: Fri, 12 Jan 2024 21:30:22 +0200 Subject: [PATCH 3/4] 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 <zin...@outlook.com> Date: Fri, 12 Jan 2024 23:28:21 +0200 Subject: [PATCH 4/4] 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; _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits