[libcxx-commits] [libcxx] 053a714 - [libc++] Implement part of P2562R1: constexpr `ranges::inplace_merge` (#131947)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Mar 19 10:59:36 PDT 2025
Author: A. Jiang
Date: 2025-03-20T01:59:32+08:00
New Revision: 053a714adda3e1d3d506f417c7fbc6eb3fa75405
URL: https://github.com/llvm/llvm-project/commit/053a714adda3e1d3d506f417c7fbc6eb3fa75405
DIFF: https://github.com/llvm/llvm-project/commit/053a714adda3e1d3d506f417c7fbc6eb3fa75405.diff
LOG: [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`.
Added:
Modified:
libcxx/include/__algorithm/ranges_inplace_merge.h
libcxx/include/algorithm
libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp
libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/include/__algorithm/ranges_inplace_merge.h b/libcxx/include/__algorithm/ranges_inplace_merge.h
index 5879d0e7ef0fb..54581aff396ea 100644
--- a/libcxx/include/__algorithm/ranges_inplace_merge.h
+++ b/libcxx/include/__algorithm/ranges_inplace_merge.h
@@ -41,7 +41,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges {
struct __inplace_merge {
template <class _Iter, class _Sent, class _Comp, class _Proj>
- _LIBCPP_HIDE_FROM_ABI static constexpr auto
+ _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX26 auto
__inplace_merge_impl(_Iter __first, _Iter __middle, _Sent __last, _Comp&& __comp, _Proj&& __proj) {
auto __last_iter = ranges::next(__middle, __last);
std::__inplace_merge<_RangeAlgPolicy>(
@@ -51,7 +51,7 @@ struct __inplace_merge {
template <bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent, class _Comp = ranges::less, class _Proj = identity>
requires sortable<_Iter, _Comp, _Proj>
- _LIBCPP_HIDE_FROM_ABI _Iter
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Iter
operator()(_Iter __first, _Iter __middle, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
return __inplace_merge_impl(
std::move(__first), std::move(__middle), std::move(__last), std::move(__comp), std::move(__proj));
@@ -59,7 +59,7 @@ struct __inplace_merge {
template <bidirectional_range _Range, class _Comp = ranges::less, class _Proj = identity>
requires sortable<iterator_t<_Range>, _Comp, _Proj>
- _LIBCPP_HIDE_FROM_ABI borrowed_iterator_t<_Range>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 borrowed_iterator_t<_Range>
operator()(_Range&& __range, iterator_t<_Range> __middle, _Comp __comp = {}, _Proj __proj = {}) const {
return __inplace_merge_impl(
ranges::begin(__range), std::move(__middle), ranges::end(__range), std::move(__comp), std::move(__proj));
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index aea24e53019cc..6ba903ad3ce1e 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -1031,13 +1031,14 @@ namespace ranges {
template<bidirectional_iterator I, sentinel_for<I> S, class Comp = ranges::less,
class Proj = identity>
requires sortable<I, Comp, Proj>
- I inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {}); // since C++20
+ constexpr I // constexpr since C++26
+ inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {}); // since C++20
template<bidirectional_range R, class Comp = ranges::less, class Proj = identity>
requires sortable<iterator_t<R>, Comp, Proj>
- borrowed_iterator_t<R>
+ constexpr borrowed_iterator_t<R> // constexpr since C++26
inplace_merge(R&& r, iterator_t<R> middle, Comp comp = {},
- Proj proj = {}); // since C++20
+ Proj proj = {}); // since C++20
template<permutable I, sentinel_for<I> S, class Proj = identity,
indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to>
diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
index 5a8640a8fc035..ee3619e0e6bcc 100644
--- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
@@ -150,8 +150,15 @@ constexpr bool all_the_algorithms()
(void)std::ranges::is_sorted(a, Less(&copies)); assert(copies == 0);
(void)std::ranges::is_sorted_until(first, last, Less(&copies)); assert(copies == 0);
(void)std::ranges::is_sorted_until(a, Less(&copies)); assert(copies == 0);
- if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(first, mid, last, Less(&copies)); assert(copies == 0); }
- if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(&copies)); assert(copies == 0); }
+#if TEST_STD_VER < 26
+ if (!std::is_constant_evaluated())
+#endif
+ {
+ (void)std::ranges::inplace_merge(first, mid, last, Less(&copies));
+ assert(copies == 0);
+ (void)std::ranges::inplace_merge(a, mid, Less(&copies));
+ assert(copies == 0);
+ }
(void)std::ranges::lexicographical_compare(first, last, first2, last2, Less(&copies)); assert(copies == 0);
(void)std::ranges::lexicographical_compare(a, b, Less(&copies)); assert(copies == 0);
(void)std::ranges::lower_bound(first, last, value, Less(&copies)); assert(copies == 0);
@@ -223,10 +230,19 @@ constexpr bool all_the_algorithms()
(void)std::ranges::sort(a, Less(&copies)); assert(copies == 0);
(void)std::ranges::sort_heap(first, last, Less(&copies)); assert(copies == 0);
(void)std::ranges::sort_heap(a, Less(&copies)); assert(copies == 0);
- if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(&copies)); assert(copies == 0); }
- if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(a, UnaryTrue(&copies)); assert(copies == 0); }
- if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(first, last, Less(&copies)); assert(copies == 0); }
- if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(a, Less(&copies)); assert(copies == 0); }
+#if TEST_STD_VER < 26
+ if (!std::is_constant_evaluated())
+#endif
+ {
+ (void)std::ranges::stable_partition(first, last, UnaryTrue(&copies));
+ assert(copies == 0);
+ (void)std::ranges::stable_partition(a, UnaryTrue(&copies));
+ assert(copies == 0);
+ (void)std::ranges::stable_sort(first, last, Less(&copies));
+ assert(copies == 0);
+ (void)std::ranges::stable_sort(a, Less(&copies));
+ assert(copies == 0);
+ }
#if TEST_STD_VER > 20
(void)std::ranges::starts_with(first, last, first2, last2, Equal(&copies)); assert(copies == 0);
(void)std::ranges::starts_with(a, b, Equal(&copies)); assert(copies == 0);
diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
index 8cdf53c183512..1a85d1f33cf10 100644
--- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
@@ -147,8 +147,15 @@ constexpr bool all_the_algorithms()
(void)std::ranges::is_sorted(a, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::is_sorted_until(first, last, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::is_sorted_until(a, Less(), Proj(&copies)); assert(copies == 0);
- if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(first, mid, last, Less(), Proj(&copies)); assert(copies == 0); }
- if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(), Proj(&copies)); assert(copies == 0); }
+#if TEST_STD_VER < 26
+ if (!std::is_constant_evaluated())
+#endif
+ {
+ (void)std::ranges::inplace_merge(first, mid, last, Less(), Proj(&copies));
+ assert(copies == 0);
+ (void)std::ranges::inplace_merge(a, mid, Less(), Proj(&copies));
+ assert(copies == 0);
+ }
(void)std::ranges::lexicographical_compare(first, last, first2, last2, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
(void)std::ranges::lexicographical_compare(a, b, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
(void)std::ranges::lower_bound(first, last, value, Less(), Proj(&copies)); assert(copies == 0);
@@ -228,10 +235,19 @@ constexpr bool all_the_algorithms()
(void)std::ranges::sort(a, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::sort_heap(first, last, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::sort_heap(a, Less(), Proj(&copies)); assert(copies == 0);
- if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
- if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(a, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
- if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(first, last, Less(), Proj(&copies)); assert(copies == 0); }
- if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(a, Less(), Proj(&copies)); assert(copies == 0); }
+#if TEST_STD_VER < 26
+ if (!std::is_constant_evaluated())
+#endif
+ {
+ (void)std::ranges::stable_partition(first, last, UnaryTrue(), Proj(&copies));
+ assert(copies == 0);
+ (void)std::ranges::stable_partition(a, UnaryTrue(), Proj(&copies));
+ assert(copies == 0);
+ (void)std::ranges::stable_sort(first, last, Less(), Proj(&copies));
+ assert(copies == 0);
+ (void)std::ranges::stable_sort(a, Less(), Proj(&copies));
+ assert(copies == 0);
+ }
#if TEST_STD_VER > 20
(void)std::ranges::starts_with(first, last, first2, last2, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);
(void)std::ranges::starts_with(a, b, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp
index 1602490893d75..827e6c1d20868 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp
@@ -13,13 +13,14 @@
// template<bidirectional_iterator I, sentinel_for<I> S, class Comp = ranges::less,
// class Proj = identity>
// requires sortable<I, Comp, Proj>
-// I inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {}); // Since C++20
+// constexpr I // constexpr since C++26
+// inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {}); // Since C++20
//
// template<bidirectional_range R, class Comp = ranges::less, class Proj = identity>
// requires sortable<iterator_t<R>, Comp, Proj>
-// borrowed_iterator_t<R>
+// constexpr borrowed_iterator_t<R> // constexpr since C++26
// inplace_merge(R&& r, iterator_t<R> middle, Comp comp = {},
-// Proj proj = {}); // Since C++20
+// Proj proj = {}); // Since C++20
#include <algorithm>
#include <array>
@@ -86,7 +87,7 @@ static_assert(!HasInplaceMergeRange<R<int*>, int*, ComparatorNotCopyable<int*>>)
static_assert(!HasInplaceMergeIter<R<const int*>, const int*>);
template <class In, template <class> class SentWrapper, std::size_t N1, std::size_t N2>
-void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int, N2> expected) {
+TEST_CONSTEXPR_CXX26 void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int, N2> expected) {
assert(std::is_sorted(input.begin(), input.begin() + midIdx));
assert(std::is_sorted(input.begin() + midIdx, input.end()));
assert(std::is_sorted(expected.begin(), expected.end()));
@@ -113,7 +114,7 @@ void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int,
}
template <class In, template <class> class SentWrapper>
-void testImpl() {
+TEST_CONSTEXPR_CXX26 void testImpl() {
// sorted range
{
std::array in{0, 1, 5, 6, 9, 10};
@@ -193,14 +194,14 @@ void testImpl() {
}
template < template <class> class SentWrapper>
-void withAllPermutationsOfIter() {
+TEST_CONSTEXPR_CXX26 void withAllPermutationsOfIter() {
testImpl<bidirectional_iterator<int*>, SentWrapper>();
testImpl<random_access_iterator<int*>, SentWrapper>();
testImpl<contiguous_iterator<int*>, SentWrapper>();
testImpl<int*, SentWrapper>();
}
-bool test() {
+TEST_CONSTEXPR_CXX26 bool test() {
withAllPermutationsOfIter<std::type_identity_t>();
withAllPermutationsOfIter<sentinel_wrapper>();
@@ -334,7 +335,9 @@ bool test() {
int main(int, char**) {
test();
- // inplace_merge is not constexpr in the latest finished Standard (C++20)
+#if TEST_STD_VER >= 26
+ static_assert(test());
+#endif
return 0;
}
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
index e4316095f28bf..12743a08e3192 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -212,8 +212,12 @@ constexpr bool test_all() {
}
dangling_1st(std::ranges::partial_sort, in, mid);
dangling_1st(std::ranges::nth_element, in, mid);
+#if TEST_STD_VER < 26
if (!std::is_constant_evaluated())
+#endif
+ {
dangling_1st(std::ranges::inplace_merge, in, mid);
+ }
dangling_1st(std::ranges::make_heap, in);
dangling_1st(std::ranges::push_heap, in);
dangling_1st(std::ranges::pop_heap, in);
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
index aec07e9626f73..914e31d133805 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
@@ -179,8 +179,12 @@ constexpr bool test_all() {
}
test_mid(std::ranges::partial_sort, in, mid, &Foo::binary_pred, &Bar::val);
test_mid(std::ranges::nth_element, in, mid, &Foo::binary_pred, &Bar::val);
+#if TEST_STD_VER < 26
if (!std::is_constant_evaluated())
+#endif
+ {
test_mid(std::ranges::inplace_merge, in, mid, &Foo::binary_pred, &Bar::val);
+ }
test(std::ranges::make_heap, in, &Foo::binary_pred, &Bar::val);
test(std::ranges::push_heap, in, &Foo::binary_pred, &Bar::val);
test(std::ranges::pop_heap, in, &Foo::binary_pred, &Bar::val);
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
index 675c2d114b3ac..2ed2cb54788a6 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
@@ -182,8 +182,12 @@ constexpr void run_tests() {
}
test_mid(std::ranges::partial_sort, in, mid);
test_mid(std::ranges::nth_element, in, mid);
+#if TEST_STD_VER < 26
if (!std::is_constant_evaluated())
+#endif
+ {
test_mid(std::ranges::inplace_merge, in, mid);
+ }
test(std::ranges::make_heap, in);
test(std::ranges::push_heap, in);
test(std::ranges::pop_heap, in);
diff --git a/libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp b/libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp
index 6bd2abded2463..32154f9eda8f6 100644
--- a/libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp
+++ b/libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp
@@ -15,23 +15,36 @@
struct A {
int i = 0;
- bool operator<(const A& rhs) const { return i < rhs.i; }
- static bool isEven(const A& a) { return a.i % 2 == 0; }
+ TEST_CONSTEXPR bool operator<(const A& rhs) const { return i < rhs.i; }
+ static TEST_CONSTEXPR bool isEven(const A& a) { return a.i % 2 == 0; }
};
void *operator new(std::size_t, A*) = delete;
-int main(int, char**)
-{
- A a[4] = {};
- std::sort(a, a+4);
- std::sort(a, a+4, std::less<A>());
- std::partition(a, a+4, A::isEven);
- std::stable_sort(a, a+4);
- std::stable_sort(a, a+4, std::less<A>());
- std::stable_partition(a, a+4, A::isEven);
- std::inplace_merge(a, a+2, a+4);
- std::inplace_merge(a, a+2, a+4, std::less<A>());
-
- return 0;
+TEST_CONSTEXPR_CXX20 bool test() {
+ A a[4] = {};
+ std::sort(a, a + 4);
+ std::sort(a, a + 4, std::less<A>());
+ std::partition(a, a + 4, A::isEven);
+#if TEST_STD_VER < 26
+ if (!TEST_IS_CONSTANT_EVALUATED)
+#endif
+ {
+ std::stable_sort(a, a + 4);
+ std::stable_sort(a, a + 4, std::less<A>());
+ std::stable_partition(a, a + 4, A::isEven);
+ std::inplace_merge(a, a + 2, a + 4);
+ std::inplace_merge(a, a + 2, a + 4, std::less<A>());
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER >= 20
+ static_assert(test());
+#endif
+
+ return 0;
}
More information about the libcxx-commits
mailing list