-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[libc++] Implement part of P2562R1: constexpr ranges::inplace_merge
#131947
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
frederick-vs-ja
merged 3 commits into
llvm:main
from
frederick-vs-ja:constexpr-ranges-inplace_merge
Mar 19, 2025
Merged
[libc++] Implement part of P2562R1: constexpr ranges::inplace_merge
#131947
frederick-vs-ja
merged 3 commits into
llvm:main
from
frederick-vs-ja:constexpr-ranges-inplace_merge
Mar 19, 2025
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. So it's better to mark it `_LIBCPP_CONSTEXPR_SINCE_CXX26`.
For `stable_partition`, `ranges::stable_sort`, `std::stable_sort`, `std::stable_partition`, and `std::inplace_merge`.
Member
|
@llvm/pr-subscribers-libcxx Author: A. Jiang (frederick-vs-ja) ChangesDrive-by changes:
Fixes #119399. Full diff: https://github.com/llvm/llvm-project/pull/131947.diff 9 Files Affected:
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;
}
|
mordante
approved these changes
Mar 19, 2025
Member
mordante
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, LGTM!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Drive-by changes:
std::__inplace_merge::__inplace_merge_impl_LIBCPP_CONSTEXPR_SINCE_CXX26.std::__inplace_mergethat is constexpr since C++26.stable_partition,ranges::stable_sort,std::stable_sort,std::stable_partition, andstd::inplace_merge.Fixes #119399.