Skip to content

Commit 053a714

Browse files
[libc++] Implement part of P2562R1: constexpr ranges::inplace_merge (#131947)
Drive-by changes: - Consistently mark `std::__inplace_merge::__inplace_merge_impl` `_LIBCPP_CONSTEXPR_SINCE_CXX26`. - This function template is only called by other functions that becomes constexpr since C++26, and it itself calls `std::__inplace_merge` that is constexpr since C++26. - Unblock related test coverage in constant evaluation for `stable_partition`, `ranges::stable_sort`, `std::stable_sort`, `std::stable_partition`, and `std::inplace_merge`.
1 parent 825460a commit 053a714

9 files changed

+102
-41
lines changed

libcxx/include/__algorithm/ranges_inplace_merge.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
4141
namespace ranges {
4242
struct __inplace_merge {
4343
template <class _Iter, class _Sent, class _Comp, class _Proj>
44-
_LIBCPP_HIDE_FROM_ABI static constexpr auto
44+
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX26 auto
4545
__inplace_merge_impl(_Iter __first, _Iter __middle, _Sent __last, _Comp&& __comp, _Proj&& __proj) {
4646
auto __last_iter = ranges::next(__middle, __last);
4747
std::__inplace_merge<_RangeAlgPolicy>(
@@ -51,15 +51,15 @@ struct __inplace_merge {
5151

5252
template <bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent, class _Comp = ranges::less, class _Proj = identity>
5353
requires sortable<_Iter, _Comp, _Proj>
54-
_LIBCPP_HIDE_FROM_ABI _Iter
54+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Iter
5555
operator()(_Iter __first, _Iter __middle, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
5656
return __inplace_merge_impl(
5757
std::move(__first), std::move(__middle), std::move(__last), std::move(__comp), std::move(__proj));
5858
}
5959

6060
template <bidirectional_range _Range, class _Comp = ranges::less, class _Proj = identity>
6161
requires sortable<iterator_t<_Range>, _Comp, _Proj>
62-
_LIBCPP_HIDE_FROM_ABI borrowed_iterator_t<_Range>
62+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 borrowed_iterator_t<_Range>
6363
operator()(_Range&& __range, iterator_t<_Range> __middle, _Comp __comp = {}, _Proj __proj = {}) const {
6464
return __inplace_merge_impl(
6565
ranges::begin(__range), std::move(__middle), ranges::end(__range), std::move(__comp), std::move(__proj));

libcxx/include/algorithm

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,13 +1031,14 @@ namespace ranges {
10311031
template<bidirectional_iterator I, sentinel_for<I> S, class Comp = ranges::less,
10321032
class Proj = identity>
10331033
requires sortable<I, Comp, Proj>
1034-
I inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {}); // since C++20
1034+
constexpr I // constexpr since C++26
1035+
inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {}); // since C++20
10351036
10361037
template<bidirectional_range R, class Comp = ranges::less, class Proj = identity>
10371038
requires sortable<iterator_t<R>, Comp, Proj>
1038-
borrowed_iterator_t<R>
1039+
constexpr borrowed_iterator_t<R> // constexpr since C++26
10391040
inplace_merge(R&& r, iterator_t<R> middle, Comp comp = {},
1040-
Proj proj = {}); // since C++20
1041+
Proj proj = {}); // since C++20
10411042
10421043
template<permutable I, sentinel_for<I> S, class Proj = identity,
10431044
indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to>

libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,15 @@ constexpr bool all_the_algorithms()
150150
(void)std::ranges::is_sorted(a, Less(&copies)); assert(copies == 0);
151151
(void)std::ranges::is_sorted_until(first, last, Less(&copies)); assert(copies == 0);
152152
(void)std::ranges::is_sorted_until(a, Less(&copies)); assert(copies == 0);
153-
if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(first, mid, last, Less(&copies)); assert(copies == 0); }
154-
if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(&copies)); assert(copies == 0); }
153+
#if TEST_STD_VER < 26
154+
if (!std::is_constant_evaluated())
155+
#endif
156+
{
157+
(void)std::ranges::inplace_merge(first, mid, last, Less(&copies));
158+
assert(copies == 0);
159+
(void)std::ranges::inplace_merge(a, mid, Less(&copies));
160+
assert(copies == 0);
161+
}
155162
(void)std::ranges::lexicographical_compare(first, last, first2, last2, Less(&copies)); assert(copies == 0);
156163
(void)std::ranges::lexicographical_compare(a, b, Less(&copies)); assert(copies == 0);
157164
(void)std::ranges::lower_bound(first, last, value, Less(&copies)); assert(copies == 0);
@@ -223,10 +230,19 @@ constexpr bool all_the_algorithms()
223230
(void)std::ranges::sort(a, Less(&copies)); assert(copies == 0);
224231
(void)std::ranges::sort_heap(first, last, Less(&copies)); assert(copies == 0);
225232
(void)std::ranges::sort_heap(a, Less(&copies)); assert(copies == 0);
226-
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(&copies)); assert(copies == 0); }
227-
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(a, UnaryTrue(&copies)); assert(copies == 0); }
228-
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(first, last, Less(&copies)); assert(copies == 0); }
229-
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(a, Less(&copies)); assert(copies == 0); }
233+
#if TEST_STD_VER < 26
234+
if (!std::is_constant_evaluated())
235+
#endif
236+
{
237+
(void)std::ranges::stable_partition(first, last, UnaryTrue(&copies));
238+
assert(copies == 0);
239+
(void)std::ranges::stable_partition(a, UnaryTrue(&copies));
240+
assert(copies == 0);
241+
(void)std::ranges::stable_sort(first, last, Less(&copies));
242+
assert(copies == 0);
243+
(void)std::ranges::stable_sort(a, Less(&copies));
244+
assert(copies == 0);
245+
}
230246
#if TEST_STD_VER > 20
231247
(void)std::ranges::starts_with(first, last, first2, last2, Equal(&copies)); assert(copies == 0);
232248
(void)std::ranges::starts_with(a, b, Equal(&copies)); assert(copies == 0);

libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,15 @@ constexpr bool all_the_algorithms()
147147
(void)std::ranges::is_sorted(a, Less(), Proj(&copies)); assert(copies == 0);
148148
(void)std::ranges::is_sorted_until(first, last, Less(), Proj(&copies)); assert(copies == 0);
149149
(void)std::ranges::is_sorted_until(a, Less(), Proj(&copies)); assert(copies == 0);
150-
if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(first, mid, last, Less(), Proj(&copies)); assert(copies == 0); }
151-
if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(), Proj(&copies)); assert(copies == 0); }
150+
#if TEST_STD_VER < 26
151+
if (!std::is_constant_evaluated())
152+
#endif
153+
{
154+
(void)std::ranges::inplace_merge(first, mid, last, Less(), Proj(&copies));
155+
assert(copies == 0);
156+
(void)std::ranges::inplace_merge(a, mid, Less(), Proj(&copies));
157+
assert(copies == 0);
158+
}
152159
(void)std::ranges::lexicographical_compare(first, last, first2, last2, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
153160
(void)std::ranges::lexicographical_compare(a, b, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
154161
(void)std::ranges::lower_bound(first, last, value, Less(), Proj(&copies)); assert(copies == 0);
@@ -228,10 +235,19 @@ constexpr bool all_the_algorithms()
228235
(void)std::ranges::sort(a, Less(), Proj(&copies)); assert(copies == 0);
229236
(void)std::ranges::sort_heap(first, last, Less(), Proj(&copies)); assert(copies == 0);
230237
(void)std::ranges::sort_heap(a, Less(), Proj(&copies)); assert(copies == 0);
231-
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
232-
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(a, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
233-
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(first, last, Less(), Proj(&copies)); assert(copies == 0); }
234-
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(a, Less(), Proj(&copies)); assert(copies == 0); }
238+
#if TEST_STD_VER < 26
239+
if (!std::is_constant_evaluated())
240+
#endif
241+
{
242+
(void)std::ranges::stable_partition(first, last, UnaryTrue(), Proj(&copies));
243+
assert(copies == 0);
244+
(void)std::ranges::stable_partition(a, UnaryTrue(), Proj(&copies));
245+
assert(copies == 0);
246+
(void)std::ranges::stable_sort(first, last, Less(), Proj(&copies));
247+
assert(copies == 0);
248+
(void)std::ranges::stable_sort(a, Less(), Proj(&copies));
249+
assert(copies == 0);
250+
}
235251
#if TEST_STD_VER > 20
236252
(void)std::ranges::starts_with(first, last, first2, last2, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);
237253
(void)std::ranges::starts_with(a, b, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);

libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313
// template<bidirectional_iterator I, sentinel_for<I> S, class Comp = ranges::less,
1414
// class Proj = identity>
1515
// requires sortable<I, Comp, Proj>
16-
// I inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {}); // Since C++20
16+
// constexpr I // constexpr since C++26
17+
// inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {}); // Since C++20
1718
//
1819
// template<bidirectional_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
// inplace_merge(R&& r, iterator_t<R> middle, Comp comp = {},
22-
// Proj proj = {}); // Since C++20
23+
// Proj proj = {}); // Since C++20
2324

2425
#include <algorithm>
2526
#include <array>
@@ -86,7 +87,7 @@ static_assert(!HasInplaceMergeRange<R<int*>, int*, ComparatorNotCopyable<int*>>)
8687
static_assert(!HasInplaceMergeIter<R<const int*>, const int*>);
8788

8889
template <class In, template <class> class SentWrapper, std::size_t N1, std::size_t N2>
89-
void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int, N2> expected) {
90+
TEST_CONSTEXPR_CXX26 void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int, N2> expected) {
9091
assert(std::is_sorted(input.begin(), input.begin() + midIdx));
9192
assert(std::is_sorted(input.begin() + midIdx, input.end()));
9293
assert(std::is_sorted(expected.begin(), expected.end()));
@@ -113,7 +114,7 @@ void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int,
113114
}
114115

115116
template <class In, template <class> class SentWrapper>
116-
void testImpl() {
117+
TEST_CONSTEXPR_CXX26 void testImpl() {
117118
// sorted range
118119
{
119120
std::array in{0, 1, 5, 6, 9, 10};
@@ -193,14 +194,14 @@ void testImpl() {
193194
}
194195

195196
template < template <class> class SentWrapper>
196-
void withAllPermutationsOfIter() {
197+
TEST_CONSTEXPR_CXX26 void withAllPermutationsOfIter() {
197198
testImpl<bidirectional_iterator<int*>, SentWrapper>();
198199
testImpl<random_access_iterator<int*>, SentWrapper>();
199200
testImpl<contiguous_iterator<int*>, SentWrapper>();
200201
testImpl<int*, SentWrapper>();
201202
}
202203

203-
bool test() {
204+
TEST_CONSTEXPR_CXX26 bool test() {
204205
withAllPermutationsOfIter<std::type_identity_t>();
205206
withAllPermutationsOfIter<sentinel_wrapper>();
206207

@@ -334,7 +335,9 @@ bool test() {
334335

335336
int main(int, char**) {
336337
test();
337-
// inplace_merge is not constexpr in the latest finished Standard (C++20)
338+
#if TEST_STD_VER >= 26
339+
static_assert(test());
340+
#endif
338341

339342
return 0;
340343
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,12 @@ constexpr bool test_all() {
212212
}
213213
dangling_1st(std::ranges::partial_sort, in, mid);
214214
dangling_1st(std::ranges::nth_element, in, mid);
215+
#if TEST_STD_VER < 26
215216
if (!std::is_constant_evaluated())
217+
#endif
218+
{
216219
dangling_1st(std::ranges::inplace_merge, in, mid);
220+
}
217221
dangling_1st(std::ranges::make_heap, in);
218222
dangling_1st(std::ranges::push_heap, in);
219223
dangling_1st(std::ranges::pop_heap, in);

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
@@ -179,8 +179,12 @@ constexpr bool test_all() {
179179
}
180180
test_mid(std::ranges::partial_sort, in, mid, &Foo::binary_pred, &Bar::val);
181181
test_mid(std::ranges::nth_element, in, mid, &Foo::binary_pred, &Bar::val);
182+
#if TEST_STD_VER < 26
182183
if (!std::is_constant_evaluated())
184+
#endif
185+
{
183186
test_mid(std::ranges::inplace_merge, in, mid, &Foo::binary_pred, &Bar::val);
187+
}
184188
test(std::ranges::make_heap, in, &Foo::binary_pred, &Bar::val);
185189
test(std::ranges::push_heap, in, &Foo::binary_pred, &Bar::val);
186190
test(std::ranges::pop_heap, in, &Foo::binary_pred, &Bar::val);

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
@@ -182,8 +182,12 @@ constexpr void run_tests() {
182182
}
183183
test_mid(std::ranges::partial_sort, in, mid);
184184
test_mid(std::ranges::nth_element, in, mid);
185+
#if TEST_STD_VER < 26
185186
if (!std::is_constant_evaluated())
187+
#endif
188+
{
186189
test_mid(std::ranges::inplace_merge, in, mid);
190+
}
187191
test(std::ranges::make_heap, in);
188192
test(std::ranges::push_heap, in);
189193
test(std::ranges::pop_heap, in);

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

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,36 @@
1515

1616
struct A {
1717
int i = 0;
18-
bool operator<(const A& rhs) const { return i < rhs.i; }
19-
static bool isEven(const A& a) { return a.i % 2 == 0; }
18+
TEST_CONSTEXPR bool operator<(const A& rhs) const { return i < rhs.i; }
19+
static TEST_CONSTEXPR bool isEven(const A& a) { return a.i % 2 == 0; }
2020
};
2121

2222
void *operator new(std::size_t, A*) = delete;
2323

24-
int main(int, char**)
25-
{
26-
A a[4] = {};
27-
std::sort(a, a+4);
28-
std::sort(a, a+4, std::less<A>());
29-
std::partition(a, a+4, A::isEven);
30-
std::stable_sort(a, a+4);
31-
std::stable_sort(a, a+4, std::less<A>());
32-
std::stable_partition(a, a+4, A::isEven);
33-
std::inplace_merge(a, a+2, a+4);
34-
std::inplace_merge(a, a+2, a+4, std::less<A>());
35-
36-
return 0;
24+
TEST_CONSTEXPR_CXX20 bool test() {
25+
A a[4] = {};
26+
std::sort(a, a + 4);
27+
std::sort(a, a + 4, std::less<A>());
28+
std::partition(a, a + 4, A::isEven);
29+
#if TEST_STD_VER < 26
30+
if (!TEST_IS_CONSTANT_EVALUATED)
31+
#endif
32+
{
33+
std::stable_sort(a, a + 4);
34+
std::stable_sort(a, a + 4, std::less<A>());
35+
std::stable_partition(a, a + 4, A::isEven);
36+
std::inplace_merge(a, a + 2, a + 4);
37+
std::inplace_merge(a, a + 2, a + 4, std::less<A>());
38+
}
39+
40+
return true;
41+
}
42+
43+
int main(int, char**) {
44+
test();
45+
#if TEST_STD_VER >= 20
46+
static_assert(test());
47+
#endif
48+
49+
return 0;
3750
}

0 commit comments

Comments
 (0)