Skip to content

Commit ba9aeed

Browse files
[libc++] Implement part of P2562R1: constexpr ranges::stable_sort (llvm#128860)
Drive-by: Enables test coverage for `ranges::stable_sort` with proxy iterators, and changes "constexpr in" to "constexpr since" in comments in `<algorithm>`.
1 parent 6e2fd4b commit ba9aeed

File tree

7 files changed

+176
-155
lines changed

7 files changed

+176
-155
lines changed

libcxx/include/__algorithm/ranges_stable_sort.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
4141
namespace ranges {
4242
struct __stable_sort {
4343
template <class _Iter, class _Sent, class _Comp, class _Proj>
44-
_LIBCPP_HIDE_FROM_ABI static _Iter __stable_sort_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
44+
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX26 _Iter
45+
__stable_sort_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
4546
auto __last_iter = ranges::next(__first, __last);
4647

4748
auto&& __projected_comp = std::__make_projected(__comp, __proj);
@@ -52,13 +53,14 @@ struct __stable_sort {
5253

5354
template <random_access_iterator _Iter, sentinel_for<_Iter> _Sent, class _Comp = ranges::less, class _Proj = identity>
5455
requires sortable<_Iter, _Comp, _Proj>
55-
_LIBCPP_HIDE_FROM_ABI _Iter operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
56+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Iter
57+
operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
5658
return __stable_sort_fn_impl(std::move(__first), std::move(__last), __comp, __proj);
5759
}
5860

5961
template <random_access_range _Range, class _Comp = ranges::less, class _Proj = identity>
6062
requires sortable<iterator_t<_Range>, _Comp, _Proj>
61-
_LIBCPP_HIDE_FROM_ABI borrowed_iterator_t<_Range>
63+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 borrowed_iterator_t<_Range>
6264
operator()(_Range&& __r, _Comp __comp = {}, _Proj __proj = {}) const {
6365
return __stable_sort_fn_impl(ranges::begin(__r), ranges::end(__r), __comp, __proj);
6466
}

libcxx/include/algorithm

Lines changed: 138 additions & 137 deletions
Large diffs are not rendered by default.

libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/ranges.stable.sort.pass.cpp

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
// template<random_access_iterator I, sentinel_for<I> S, class Comp = ranges::less,
1414
// class Proj = identity>
1515
// requires sortable<I, Comp, Proj>
16-
// I ranges::stable_sort(I first, S last, Comp comp = {}, Proj proj = {}); // since C++20
16+
// constexpr I // constexpr since C++26
17+
// ranges::stable_sort(I first, S last, Comp comp = {}, Proj proj = {}); // since C++20
1718
//
1819
// template<random_access_range R, class Comp = ranges::less, class Proj = identity>
1920
// requires sortable<iterator_t<R>, Comp, Proj>
20-
// borrowed_iterator_t<R>
21+
// constexpr borrowed_iterator_t<R> // constexpr since C++26
2122
// ranges::stable_sort(R&& r, Comp comp = {}, Proj proj = {}); // since C++20
2223

2324
#include <algorithm>
@@ -57,7 +58,7 @@ static_assert(!HasStableSortR<UncheckedRange<int*>, BadComparator>);
5758
static_assert(!HasStableSortR<UncheckedRange<const int*>>); // Doesn't satisfy `sortable`.
5859

5960
template <class Iter, class Sent, std::size_t N>
60-
void test_one(std::array<int, N> input, std::array<int, N> expected) {
61+
TEST_CONSTEXPR_CXX26 void test_one(std::array<int, N> input, std::array<int, N> expected) {
6162
{ // (iterator, sentinel) overload.
6263
auto sorted = input;
6364
auto b = Iter(sorted.data());
@@ -81,7 +82,7 @@ void test_one(std::array<int, N> input, std::array<int, N> expected) {
8182
}
8283

8384
template <class Iter, class Sent>
84-
void test_iterators_2() {
85+
TEST_CONSTEXPR_CXX26 void test_iterators_2() {
8586
// Empty sequence.
8687
test_one<Iter, Sent, 0>({}, {});
8788
// 1-element sequence.
@@ -105,25 +106,25 @@ void test_iterators_2() {
105106
}
106107

107108
template <class Iter>
108-
void test_iterators_1() {
109+
TEST_CONSTEXPR_CXX26 void test_iterators_1() {
109110
test_iterators_2<Iter, Iter>();
110111
test_iterators_2<Iter, sentinel_wrapper<Iter>>();
111112
}
112113

113-
void test_iterators() {
114+
TEST_CONSTEXPR_CXX26 void test_iterators() {
114115
test_iterators_1<random_access_iterator<int*>>();
115116
test_iterators_1<contiguous_iterator<int*>>();
116117
test_iterators_1<int*>();
117118
}
118119

119-
void test() {
120+
TEST_CONSTEXPR_CXX26 bool test() {
120121
test_iterators();
121122

122123
struct OrderedValue {
123124
int value;
124125
double original_order;
125126
bool operator==(const OrderedValue&) const = default;
126-
auto operator<=>(const OrderedValue& rhs) const { return value <=> rhs.value; }
127+
TEST_CONSTEXPR_CXX26 auto operator<=>(const OrderedValue& rhs) const { return value <=> rhs.value; }
127128
};
128129

129130
{ // The sort is stable (equivalent elements remain in the same order).
@@ -214,10 +215,10 @@ void test() {
214215
{ // `std::invoke` is used in the implementation.
215216
struct S {
216217
int i;
217-
S(int i_) : i(i_) {}
218+
TEST_CONSTEXPR_CXX26 S(int i_) : i(i_) {}
218219

219-
bool comparator(const S& rhs) const { return i < rhs.i; }
220-
const S& projection() const { return *this; }
220+
TEST_CONSTEXPR_CXX26 bool comparator(const S& rhs) const { return i < rhs.i; }
221+
TEST_CONSTEXPR_CXX26 const S& projection() const { return *this; }
221222

222223
bool operator==(const S&) const = default;
223224
};
@@ -242,8 +243,6 @@ void test() {
242243
std::ranges::stable_sort(std::array{1, 2, 3});
243244
}
244245

245-
// TODO: Enable the tests once the implementation switched to use iter_move/iter_swap
246-
/*
247246
{ // ProxyIterator
248247
{
249248
std::array in = {2, 1, 3};
@@ -260,12 +259,15 @@ void test() {
260259
assert((in == std::array{1, 2, 3}));
261260
}
262261
}
263-
*/
262+
263+
return true;
264264
}
265265

266266
int main(int, char**) {
267267
test();
268-
// Note: `stable_sort` is not `constexpr`.
268+
#if TEST_STD_VER >= 26
269+
static_assert(test());
270+
#endif
269271

270272
return 0;
271273
}

libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,12 @@ constexpr bool test_all() {
204204
dangling_1st(std::ranges::stable_partition, in, unary_pred);
205205
}
206206
dangling_1st(std::ranges::sort, in);
207+
#if TEST_STD_VER < 26
207208
if (!std::is_constant_evaluated())
209+
#endif
210+
{
208211
dangling_1st(std::ranges::stable_sort, in);
212+
}
209213
dangling_1st(std::ranges::partial_sort, in, mid);
210214
dangling_1st(std::ranges::nth_element, in, mid);
211215
if (!std::is_constant_evaluated())

libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,12 @@ constexpr bool test_all() {
171171
test(std::ranges::stable_partition, in, &Foo::unary_pred, &Bar::val);
172172
}
173173
test(std::ranges::sort, in, &Foo::binary_pred, &Bar::val);
174+
#if TEST_STD_VER < 26
174175
if (!std::is_constant_evaluated())
176+
#endif
177+
{
175178
test(std::ranges::stable_sort, in, &Foo::binary_pred, &Bar::val);
179+
}
176180
test_mid(std::ranges::partial_sort, in, mid, &Foo::binary_pred, &Bar::val);
177181
test_mid(std::ranges::nth_element, in, mid, &Foo::binary_pred, &Bar::val);
178182
if (!std::is_constant_evaluated())

libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,12 @@ constexpr void run_tests() {
174174
test(std::ranges::stable_partition, in, unary_pred);
175175
}
176176
test(std::ranges::sort, in);
177+
#if TEST_STD_VER < 26
177178
if (!std::is_constant_evaluated())
179+
#endif
180+
{
178181
test(std::ranges::stable_sort, in);
182+
}
179183
test_mid(std::ranges::partial_sort, in, mid);
180184
test_mid(std::ranges::nth_element, in, mid);
181185
if (!std::is_constant_evaluated())

libcxx/test/support/test_iterators.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,10 @@ struct Proxy {
12081208

12091209
constexpr const T&& getData() const&& { return static_cast<const T&&>(data); }
12101210

1211+
// Explicitly declare the copy constructor as defaulted to avoid deprecation of the implicitly declared one
1212+
// because of the user-provided copy assignment operator.
1213+
Proxy(const Proxy&) = default;
1214+
12111215
template <class U>
12121216
requires std::constructible_from<T, U&&>
12131217
constexpr Proxy(U&& u) : data{std::forward<U>(u)} {}

0 commit comments

Comments
 (0)