Skip to content

Commit 03c19e9

Browse files
H-G-HristovZingammordante
authored
[libc++][numeric] P0543R3: Saturation arithmetic (#77967)
Implements: https://wg21.link/P0543R3 - https://eel.is/c++draft/numeric.sat Additional references: - Division: https://eel.is/c++draft/expr.mul#4 - Arithmetic conversions: https://eel.is/c++draft/expr.arith.conv#1 - Clang builtins: https://clang.llvm.org/docs/LanguageExtensions.html#builtin-functions Depends on: #78086 --------- Co-authored-by: Zingam <[email protected]> Co-authored-by: Mark de Wever <[email protected]>
1 parent 85337df commit 03c19e9

24 files changed

+1697
-29
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ Status
432432
--------------------------------------------------- -----------------
433433
``__cpp_lib_rcu`` *unimplemented*
434434
--------------------------------------------------- -----------------
435-
``__cpp_lib_saturation_arithmetic`` *unimplemented*
435+
``__cpp_lib_saturation_arithmetic`` ``202311L``
436436
--------------------------------------------------- -----------------
437437
``__cpp_lib_smart_ptr_owner_equality`` *unimplemented*
438438
--------------------------------------------------- -----------------

libcxx/docs/ReleaseNotes/18.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Implemented Papers
7575
- P2909R4 - Fix formatting of code units as integers (Dude, where’s my ``char``?)
7676
- P2821R5 - ``span.at()``
7777
- P0521R0 - Proposed Resolution for CA 14 (``shared_ptr`` ``use_count/unique``)
78+
- P0543R3 - Saturation arithmetic
7879
- P1759R6 - Native handles and file streams
7980
- P2868R3 - Remove Deprecated ``std::allocator`` Typedef From C++26
8081
- P2517R1 - Add a conditional ``noexcept`` specification to ``std::apply``

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"`P2714R1 <https://wg21.link/P2714R1>`__","LWG","Bind front and back to NTTP callables","Varna June 2023","","",""
2828
"`P2630R4 <https://wg21.link/P2630R4>`__","LWG","``submdspan``","Varna June 2023","","",""
2929
"","","","","","",""
30-
"`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","","",""
30+
"`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","|Complete|","18.0",""
3131
"`P2407R5 <https://wg21.link/P2407R5>`__","LWG","Freestanding Library: Partial Classes","Kona November 2023","","",""
3232
"`P2546R5 <https://wg21.link/P2546R5>`__","LWG","Debugging Support","Kona November 2023","","",""
3333
"`P2905R2 <https://wg21.link/P2905R2>`__","LWG","Runtime format strings","Kona November 2023","|Complete|","18.0","|format| |DR|"

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ set(files
569569
__numeric/pstl_reduce.h
570570
__numeric/pstl_transform_reduce.h
571571
__numeric/reduce.h
572+
__numeric/saturation_arithmetic.h
572573
__numeric/transform_exclusive_scan.h
573574
__numeric/transform_inclusive_scan.h
574575
__numeric/transform_reduce.h
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP___NUMERIC_SATURATION_ARITHMETIC_H
11+
#define _LIBCPP___NUMERIC_SATURATION_ARITHMETIC_H
12+
13+
#include <__concepts/arithmetic.h>
14+
#include <__config>
15+
#include <__utility/cmp.h>
16+
#include <limits>
17+
18+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
19+
# pragma GCC system_header
20+
#endif
21+
22+
_LIBCPP_BEGIN_NAMESPACE_STD
23+
24+
#if _LIBCPP_STD_VER >= 26
25+
26+
template <__libcpp_integer _Tp>
27+
_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
28+
if (_Tp __sum; !__builtin_add_overflow(__x, __y, &__sum))
29+
return __sum;
30+
// Handle overflow
31+
if constexpr (__libcpp_unsigned_integer<_Tp>) {
32+
return std::numeric_limits<_Tp>::max();
33+
} else {
34+
// Signed addition overflow
35+
if (__x > 0)
36+
// Overflows if (x > 0 && y > 0)
37+
return std::numeric_limits<_Tp>::max();
38+
else
39+
// Overflows if (x < 0 && y < 0)
40+
return std::numeric_limits<_Tp>::min();
41+
}
42+
}
43+
44+
template <__libcpp_integer _Tp>
45+
_LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
46+
if (_Tp __sub; !__builtin_sub_overflow(__x, __y, &__sub))
47+
return __sub;
48+
// Handle overflow
49+
if constexpr (__libcpp_unsigned_integer<_Tp>) {
50+
// Overflows if (x < y)
51+
return std::numeric_limits<_Tp>::min();
52+
} else {
53+
// Signed subtration overflow
54+
if (__x >= 0)
55+
// Overflows if (x >= 0 && y < 0)
56+
return std::numeric_limits<_Tp>::max();
57+
else
58+
// Overflows if (x < 0 && y > 0)
59+
return std::numeric_limits<_Tp>::min();
60+
}
61+
}
62+
63+
template <__libcpp_integer _Tp>
64+
_LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
65+
if (_Tp __mul; !__builtin_mul_overflow(__x, __y, &__mul))
66+
return __mul;
67+
// Handle overflow
68+
if constexpr (__libcpp_unsigned_integer<_Tp>) {
69+
return std::numeric_limits<_Tp>::max();
70+
} else {
71+
// Signed multiplication overflow
72+
if ((__x > 0 && __y > 0) || (__x < 0 && __y < 0))
73+
return std::numeric_limits<_Tp>::max();
74+
// Overflows if (x < 0 && y > 0) || (x > 0 && y < 0)
75+
return std::numeric_limits<_Tp>::min();
76+
}
77+
}
78+
79+
template <__libcpp_integer _Tp>
80+
_LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
81+
_LIBCPP_ASSERT_UNCATEGORIZED(__y != 0, "Division by 0 is undefined");
82+
if constexpr (__libcpp_unsigned_integer<_Tp>) {
83+
return __x / __y;
84+
} else {
85+
// Handle signed division overflow
86+
if (__x == std::numeric_limits<_Tp>::min() && __y == _Tp{-1})
87+
return std::numeric_limits<_Tp>::max();
88+
return __x / __y;
89+
}
90+
}
91+
92+
template <__libcpp_integer _Rp, __libcpp_integer _Tp>
93+
_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
94+
// Saturation is impossible edge case when ((min _Rp) < (min _Tp) && (max _Rp) > (max _Tp)) and it is expected to be
95+
// optimized out by the compiler.
96+
97+
// Handle overflow
98+
if (std::cmp_less(__x, std::numeric_limits<_Rp>::min()))
99+
return std::numeric_limits<_Rp>::min();
100+
if (std::cmp_greater(__x, std::numeric_limits<_Rp>::max()))
101+
return std::numeric_limits<_Rp>::max();
102+
// No overflow
103+
return static_cast<_Rp>(__x);
104+
}
105+
106+
#endif // _LIBCPP_STD_VER >= 26
107+
108+
_LIBCPP_END_NAMESPACE_STD
109+
110+
#endif // _LIBCPP___NUMERIC_SATURATION_ARITHMETIC_H

libcxx/include/libcxx.imp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@
559559
{ include: [ "<__numeric/pstl_reduce.h>", "private", "<numeric>", "public" ] },
560560
{ include: [ "<__numeric/pstl_transform_reduce.h>", "private", "<numeric>", "public" ] },
561561
{ include: [ "<__numeric/reduce.h>", "private", "<numeric>", "public" ] },
562+
{ include: [ "<__numeric/saturation_arithmetic.h>", "private", "<numeric>", "public" ] },
562563
{ include: [ "<__numeric/transform_exclusive_scan.h>", "private", "<numeric>", "public" ] },
563564
{ include: [ "<__numeric/transform_inclusive_scan.h>", "private", "<numeric>", "public" ] },
564565
{ include: [ "<__numeric/transform_reduce.h>", "private", "<numeric>", "public" ] },

libcxx/include/module.modulemap.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,7 @@ module std_private_numeric_pstl_transform_reduce [system] {
15801580
export *
15811581
}
15821582
module std_private_numeric_reduce [system] { header "__numeric/reduce.h" }
1583+
module std_private_numeric_saturation_arithmetic [system] { header "__numeric/saturation_arithmetic.h" }
15831584
module std_private_numeric_transform_exclusive_scan [system] { header "__numeric/transform_exclusive_scan.h" }
15841585
module std_private_numeric_transform_inclusive_scan [system] { header "__numeric/transform_inclusive_scan.h" }
15851586
module std_private_numeric_transform_reduce [system] { header "__numeric/transform_reduce.h" }

libcxx/include/numeric

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,18 @@ template<class T>
140140
template<class T>
141141
constexpr T* midpoint(T* a, T* b); // C++20
142142
143+
// [numeric.sat], saturation arithmetic
144+
template<class T>
145+
constexpr T add_sat(T x, T y) noexcept; // freestanding, Since C++26
146+
template<class T>
147+
constexpr T sub_sat(T x, T y) noexcept; // freestanding, Since C++26
148+
template<class T>
149+
constexpr T mul_sat(T x, T y) noexcept; // freestanding, Since C++26
150+
template<class T>
151+
constexpr T div_sat(T x, T y) noexcept; // freestanding, Since C++26
152+
template<class T, class U>
153+
constexpr T saturate_cast(U x) noexcept; // freestanding, Since C++26
154+
143155
} // std
144156
145157
*/
@@ -160,6 +172,7 @@ template<class T>
160172
#include <__numeric/pstl_reduce.h>
161173
#include <__numeric/pstl_transform_reduce.h>
162174
#include <__numeric/reduce.h>
175+
#include <__numeric/saturation_arithmetic.h>
163176
#include <__numeric/transform_exclusive_scan.h>
164177
#include <__numeric/transform_inclusive_scan.h>
165178
#include <__numeric/transform_reduce.h>

libcxx/include/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
504504
// # define __cpp_lib_out_ptr 202311L
505505
# define __cpp_lib_ratio 202306L
506506
// # define __cpp_lib_rcu 202306L
507-
// # define __cpp_lib_saturation_arithmetic 202311L
507+
# define __cpp_lib_saturation_arithmetic 202311L
508508
// # define __cpp_lib_smart_ptr_owner_equality 202306L
509509
# define __cpp_lib_span_at 202311L
510510
# define __cpp_lib_span_initializer_list 202311L

libcxx/modules/std/numeric.inc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,14 @@ export namespace std {
5454

5555
// [numeric.ops.midpoint], midpoint
5656
using std::midpoint;
57+
58+
#if _LIBCPP_STD_VER >= 26
59+
// [numeric.sat], saturation arithmetic
60+
using std::add_sat;
61+
using std::div_sat;
62+
using std::mul_sat;
63+
using std::saturate_cast;
64+
using std::sub_sat;
65+
#endif
66+
5767
} // namespace std

libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -263,17 +263,11 @@
263263
# endif
264264
# endif
265265

266-
# if !defined(_LIBCPP_VERSION)
267-
# ifndef __cpp_lib_saturation_arithmetic
268-
# error "__cpp_lib_saturation_arithmetic should be defined in c++26"
269-
# endif
270-
# if __cpp_lib_saturation_arithmetic != 202311L
271-
# error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
272-
# endif
273-
# else // _LIBCPP_VERSION
274-
# ifdef __cpp_lib_saturation_arithmetic
275-
# error "__cpp_lib_saturation_arithmetic should not be defined because it is unimplemented in libc++!"
276-
# endif
266+
# ifndef __cpp_lib_saturation_arithmetic
267+
# error "__cpp_lib_saturation_arithmetic should be defined in c++26"
268+
# endif
269+
# if __cpp_lib_saturation_arithmetic != 202311L
270+
# error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
277271
# endif
278272

279273
#endif // TEST_STD_VER > 23

libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7167,17 +7167,11 @@
71677167
# error "__cpp_lib_sample should have the value 201603L in c++26"
71687168
# endif
71697169

7170-
# if !defined(_LIBCPP_VERSION)
7171-
# ifndef __cpp_lib_saturation_arithmetic
7172-
# error "__cpp_lib_saturation_arithmetic should be defined in c++26"
7173-
# endif
7174-
# if __cpp_lib_saturation_arithmetic != 202311L
7175-
# error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
7176-
# endif
7177-
# else // _LIBCPP_VERSION
7178-
# ifdef __cpp_lib_saturation_arithmetic
7179-
# error "__cpp_lib_saturation_arithmetic should not be defined because it is unimplemented in libc++!"
7180-
# endif
7170+
# ifndef __cpp_lib_saturation_arithmetic
7171+
# error "__cpp_lib_saturation_arithmetic should be defined in c++26"
7172+
# endif
7173+
# if __cpp_lib_saturation_arithmetic != 202311L
7174+
# error "__cpp_lib_saturation_arithmetic should have the value 202311L in c++26"
71817175
# endif
71827176

71837177
# ifndef __cpp_lib_scoped_lock
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
10+
11+
// <numeric>
12+
13+
// template<class T>
14+
// constexpr T add_sat(T x, T y) noexcept; // freestanding
15+
16+
#include <concepts>
17+
#include <numeric>
18+
19+
#include "test_macros.h"
20+
21+
template <typename T, typename U>
22+
concept CanDo = requires(T x, U y) {
23+
{ std::add_sat(x, y) } -> std::same_as<T>;
24+
};
25+
26+
template <typename T, typename U>
27+
constexpr void test_constraint_success() {
28+
static_assert(CanDo<T, T>);
29+
static_assert(!CanDo<U, T>);
30+
static_assert(!CanDo<T, U>);
31+
}
32+
33+
template <typename T>
34+
constexpr void test_constraint_fail() {
35+
using I = int;
36+
static_assert(!CanDo<T, T>);
37+
static_assert(!CanDo<I, T>);
38+
static_assert(!CanDo<T, I>);
39+
}
40+
41+
constexpr void test() {
42+
// Contraint success - Signed
43+
using SI = long long int;
44+
test_constraint_success<signed char, SI>();
45+
test_constraint_success<short int, SI>();
46+
test_constraint_success<signed char, SI>();
47+
test_constraint_success<short int, SI>();
48+
test_constraint_success<int, SI>();
49+
test_constraint_success<long int, SI>();
50+
test_constraint_success<long long int, int>();
51+
#ifndef TEST_HAS_NO_INT128
52+
test_constraint_success<__int128_t, SI>();
53+
#endif
54+
// Contraint success - Unsigned
55+
using UI = unsigned long long int;
56+
test_constraint_success<unsigned char, UI>();
57+
test_constraint_success<unsigned short int, UI>();
58+
test_constraint_success<unsigned int, UI>();
59+
test_constraint_success<unsigned long int, UI>();
60+
test_constraint_success<unsigned long long int, unsigned int>();
61+
#ifndef TEST_HAS_NO_INT128
62+
test_constraint_success<__uint128_t, UI>();
63+
#endif
64+
65+
// Contraint failure
66+
test_constraint_fail<bool>();
67+
test_constraint_fail<char>();
68+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
69+
test_constraint_fail<wchar_t>();
70+
#endif
71+
test_constraint_fail<char8_t>();
72+
test_constraint_fail<char16_t>();
73+
test_constraint_fail<char32_t>();
74+
test_constraint_fail<float>();
75+
test_constraint_fail<double>();
76+
test_constraint_fail<long double>();
77+
}

0 commit comments

Comments
 (0)