Skip to content

Commit 48c4463

Browse files
committed
[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
1 parent e27561f commit 48c4463

23 files changed

+1026
-32
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: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ Implemented Papers
5454
- P2905R2 - Runtime format strings
5555
- P2918R2 - Runtime format strings II
5656
- P2871R3 - Remove Deprecated Unicode Conversion Facets from C++26
57-
- P2870R3 - Remove basic_string::reserve()
57+
- P2870R3 - Remove ``basic_string::reserve()``
5858
- P2909R4 - Fix formatting of code units as integers (Dude, where’s my ``char``?)
59-
- P2821R5 - span.at()
60-
- P0521R0 - Proposed Resolution for CA 14 (shared_ptr use_count/unique)
59+
- P2821R5 - ``span.at()``
60+
- P0521R0 - Proposed Resolution for CA 14 (``shared_ptr`` ``use_count/unique``)
6161
- P1759R6 - Native handles and file streams
6262
- P2517R1 - Add a conditional ``noexcept`` specification to ``std::apply``
63+
- P0543R3 - Saturation arithmetic
6364

6465

6566
Improvements and New Features

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: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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 <__type_traits/decay.h>
16+
#include <__utility/cmp.h>
17+
#include <limits>
18+
19+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
20+
# pragma GCC system_header
21+
#endif
22+
23+
_LIBCPP_BEGIN_NAMESPACE_STD
24+
25+
#if _LIBCPP_STD_VER >= 26
26+
27+
template <typename _Tp>
28+
// concept __libcpp_standard_integer = __libcpp_unsigned_integer<_Tp> || __libcpp_signed_integer<_Tp>;
29+
// concept __libcpp_standard_integer =
30+
// __libcpp_unsigned_integer<remove_cv<_Tp>> || __libcpp_signed_integer<remove_cv<_Tp>>;
31+
concept __libcpp_standard_integer = __libcpp_unsigned_integer<decay_t<_Tp>> || __libcpp_signed_integer<decay_t<_Tp>>;
32+
33+
template <__libcpp_standard_integer _Tp>
34+
// requires __libcpp_standard_integer<_Tp>
35+
_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
36+
// builtins: clang/docs/LanguageExtensions.rst
37+
// builtins:
38+
// https://github.com/llvm/llvm-project/blob/7b45c549670a8e8b6fe90f4382b0699dd20707d3/clang/docs/LanguageExtensions.rst#L3500
39+
if (_Tp __sum; !__builtin_add_overflow(__x, __y, &__sum))
40+
return __sum;
41+
// Handle overflow
42+
if constexpr (__libcpp_unsigned_integer<_Tp>) {
43+
return std::numeric_limits<_Tp>::max();
44+
} else {
45+
// Signed addition overflow
46+
if (__x > 0)
47+
// Overflows if (x > 0 && y > 0)
48+
return std::numeric_limits<_Tp>::max();
49+
else
50+
// Overflows if (x < 0 && y < 0)
51+
return std::numeric_limits<_Tp>::min();
52+
}
53+
}
54+
55+
template <__libcpp_standard_integer _Tp>
56+
_LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
57+
if (_Tp __sub; !__builtin_sub_overflow(__x, __y, &__sub))
58+
return __sub;
59+
// Handle overflow
60+
if constexpr (__libcpp_unsigned_integer<_Tp>) {
61+
// Overflows if (x < y)
62+
return std::numeric_limits<_Tp>::min();
63+
} else {
64+
// Signed subtration overflow
65+
if (__x > 0)
66+
// Overflows if (x > 0 && y < 0)
67+
return std::numeric_limits<_Tp>::max();
68+
else
69+
// Overflows if (x < 0 && y > 0)
70+
return std::numeric_limits<_Tp>::min();
71+
}
72+
}
73+
74+
template <__libcpp_standard_integer _Tp>
75+
_LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
76+
if (_Tp __mul; !__builtin_mul_overflow(__x, __y, &__mul))
77+
return __mul;
78+
// Handle overflow
79+
if constexpr (__libcpp_unsigned_integer<_Tp>) {
80+
return std::numeric_limits<_Tp>::max();
81+
} else {
82+
// Signed multiplication overflow
83+
// if (__x > 0 && __y > 0)
84+
// // Overflows if (x > 0 && y > 0)
85+
// return std::numeric_limits<_Tp>::max();
86+
// else if (__y > 0)
87+
// // Overflows if (x > 0 && y < 0)
88+
// return std::numeric_limits<_Tp>::max();
89+
if (__x > 0) {
90+
if (__y > 0)
91+
// Overflows if (x > 0 && y > 0)
92+
return std::numeric_limits<_Tp>::max();
93+
// Overflows if (x > 0 && y < 0)
94+
return std::numeric_limits<_Tp>::min();
95+
}
96+
if (__y > 0)
97+
// Overflows if (x < 0 && y > 0)
98+
return std::numeric_limits<_Tp>::min();
99+
// Overflows if (x < 0 && y < 0)
100+
return std::numeric_limits<_Tp>::max();
101+
}
102+
}
103+
104+
template <__libcpp_standard_integer _Tp>
105+
_LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
106+
_LIBCPP_ASSERT_UNCATEGORIZED(__y != 0, "Division by 0 is undefined");
107+
if constexpr (__libcpp_unsigned_integer<_Tp>) {
108+
return __x / __y;
109+
} else {
110+
// Handle signed division overflow
111+
if (__x == std::numeric_limits<_Tp>::min() && __y == _Tp{-1})
112+
return std::numeric_limits<_Tp>::max();
113+
return __x / __y;
114+
}
115+
}
116+
117+
template <__libcpp_standard_integer _Rp, __libcpp_standard_integer _Tp>
118+
_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
119+
// if (std::in_range<_Rp>(__x)) {
120+
// return _Rp{__x};
121+
// }
122+
// Handle overflow
123+
if (std::cmp_less_equal(__x, std::numeric_limits<_Rp>::min()))
124+
return std::numeric_limits<_Rp>::min();
125+
if (std::cmp_greater_equal(__x, std::numeric_limits<_Rp>::max()))
126+
return std::numeric_limits<_Rp>::max();
127+
// No overflow
128+
return static_cast<_Rp>(__x);
129+
}
130+
131+
#endif // _LIBCPP_STD_VER >= 26
132+
133+
_LIBCPP_END_NAMESPACE_STD
134+
135+
#endif // _LIBCPP___NUMERIC_SATURATION_ARITHMETIC_H

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::sub_sat;
62+
using std::mul_sat;
63+
using std::div_sat;
64+
using std::saturate_cast;
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

0 commit comments

Comments
 (0)