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 01/13] [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 02/13] 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 03/13] 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 04/13] 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 <zin...@outlook.com> Date: Sat, 13 Jan 2024 21:27:23 +0200 Subject: [PATCH 05/13] 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 <zin...@outlook.com> Date: Sat, 13 Jan 2024 22:54:35 +0200 Subject: [PATCH 06/13] 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 <zin...@outlook.com> Date: Sat, 13 Jan 2024 23:39:24 +0200 Subject: [PATCH 07/13] 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 <zin...@outlook.com> Date: Sun, 14 Jan 2024 00:36:53 +0200 Subject: [PATCH 08/13] 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 <zin...@outlook.com> Date: Sun, 14 Jan 2024 10:11:05 +0200 Subject: [PATCH 09/13] 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 <zin...@outlook.com> Date: Sun, 14 Jan 2024 10:57:54 +0200 Subject: [PATCH 10/13] 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 <zin...@outlook.com> Date: Sun, 14 Jan 2024 11:14:38 +0200 Subject: [PATCH 11/13] 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 <zin...@outlook.com> Date: Sun, 14 Jan 2024 11:25:17 +0200 Subject: [PATCH 12/13] 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 <zin...@outlook.com> Date: Sun, 14 Jan 2024 12:18:07 +0200 Subject: [PATCH 13/13] 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(); } } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits