Skip to content

[libc++] Implement part of P2562R1: constexpr std::inplace_merge #129008

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
merged 20 commits into from
Mar 18, 2025

Conversation

frederick-vs-ja
Copy link
Contributor

@frederick-vs-ja frederick-vs-ja commented Feb 27, 2025

Fixes #119398.

Drive-by:

  • Adds constexpr_random.h for pseudo-randomizing or shuffling in tests for constant evaluation.

@frederick-vs-ja frederick-vs-ja requested a review from a team as a code owner February 27, 2025 06:09
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Feb 27, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 27, 2025

@llvm/pr-subscribers-libcxx

Author: A. Jiang (frederick-vs-ja)

Changes

Fixes #119398.


Patch is 22.96 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/129008.diff

6 Files Affected:

  • (modified) libcxx/include/__algorithm/inplace_merge.h (+3-3)
  • (modified) libcxx/include/algorithm (+2-2)
  • (modified) libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge.pass.cpp (+121-80)
  • (modified) libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp (+174-108)
  • (modified) libcxx/test/std/algorithms/robust_re_difference_type.compile.pass.cpp (+7-2)
  • (modified) libcxx/test/support/counting_predicates.h (+14-12)
diff --git a/libcxx/include/__algorithm/inplace_merge.h b/libcxx/include/__algorithm/inplace_merge.h
index 1fc31b66f4bd6..ca49196320981 100644
--- a/libcxx/include/__algorithm/inplace_merge.h
+++ b/libcxx/include/__algorithm/inplace_merge.h
@@ -203,7 +203,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void __inplace_merge(
 }
 
 template <class _AlgPolicy, class _BidirectionalIterator, class _Compare>
-_LIBCPP_HIDE_FROM_ABI void __inplace_merge(
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __inplace_merge(
     _BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last, _Compare&& __comp) {
   typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
   typedef typename iterator_traits<_BidirectionalIterator>::difference_type difference_type;
@@ -223,14 +223,14 @@ _LIBCPP_HIDE_FROM_ABI void __inplace_merge(
 }
 
 template <class _BidirectionalIterator, class _Compare>
-inline _LIBCPP_HIDE_FROM_ABI void inplace_merge(
+_LIBCPP_HIDE_FROM_ABI inline _LIBCPP_CONSTEXPR_SINCE_CXX26 void inplace_merge(
     _BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last, _Compare __comp) {
   std::__inplace_merge<_ClassicAlgPolicy>(
       std::move(__first), std::move(__middle), std::move(__last), static_cast<__comp_ref_type<_Compare> >(__comp));
 }
 
 template <class _BidirectionalIterator>
-inline _LIBCPP_HIDE_FROM_ABI void
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last) {
   std::inplace_merge(std::move(__first), std::move(__middle), std::move(__last), __less<>());
 }
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 7b4cb8e496196..916d162c9fa86 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -1606,11 +1606,11 @@ template <class InputIterator1, class InputIterator2, class OutputIterator, clas
           InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);
 
 template <class BidirectionalIterator>
-    void
+    constexpr void                                    // constexpr in C++26
     inplace_merge(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last);
 
 template <class BidirectionalIterator, class Compare>
-    void
+    constexpr void                                    // constexpr in C++26
     inplace_merge(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last, Compare comp);
 
 template <class InputIterator1, class InputIterator2>
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge.pass.cpp
index 87bf9dc3854b3..21ade1d9fcfeb 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge.pass.cpp
@@ -8,11 +8,9 @@
 
 // <algorithm>
 
-// template<BidirectionalIterator Iter>
-//   requires ShuffleIterator<Iter>
-//         && LessThanComparable<Iter::value_type>
-//   void
-//   inplace_merge(Iter first, Iter middle, Iter last);
+// template<class BidirectionalIterator>
+//   constexpr void                             // constexpr since C++26
+//   inplace_merge(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last);
 
 #include <algorithm>
 #include <cassert>
@@ -25,99 +23,142 @@
 
 #if TEST_STD_VER >= 11
 struct S {
-    S() : i_(0) {}
-    S(int i) : i_(i) {}
+  TEST_CONSTEXPR_CXX26 S() : i_(0) {}
+  TEST_CONSTEXPR_CXX26 S(int i) : i_(i) {}
+
+  TEST_CONSTEXPR_CXX26 S(const S& rhs) : i_(rhs.i_) {}
+  TEST_CONSTEXPR_CXX26 S(S&& rhs) : i_(rhs.i_) { rhs.i_ = -1; }
+
+  TEST_CONSTEXPR_CXX26 S& operator=(const S& rhs) {
+    i_ = rhs.i_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX26 S& operator=(S&& rhs) {
+    i_     = rhs.i_;
+    rhs.i_ = -2;
+    assert(this != &rhs);
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX26 S& operator=(int i) {
+    i_ = i;
+    return *this;
+  }
+
+  TEST_CONSTEXPR_CXX26 bool operator<(const S& rhs) const { return i_ < rhs.i_; }
+  TEST_CONSTEXPR_CXX26 bool operator==(const S& rhs) const { return i_ == rhs.i_; }
+  TEST_CONSTEXPR_CXX26 bool operator==(int i) const { return i_ == i; }
+
+  TEST_CONSTEXPR_CXX26 void set(int i) { i_ = i; }
+
+  int i_;
+};
+#endif // TEST_STD_VER >= 11
 
-    S(const S&  rhs) : i_(rhs.i_) {}
-    S(      S&& rhs) : i_(rhs.i_) { rhs.i_ = -1; }
-
-    S& operator =(const S&  rhs) { i_ = rhs.i_;              return *this; }
-    S& operator =(      S&& rhs) { i_ = rhs.i_; rhs.i_ = -2; assert(this != &rhs); return *this; }
-    S& operator =(int i)         { i_ = i;                   return *this; }
-
-    bool operator  <(const S&  rhs) const { return i_ < rhs.i_; }
-    bool operator ==(const S&  rhs) const { return i_ == rhs.i_; }
-    bool operator ==(int i)         const { return i_ == i; }
-
-    void set(int i) { i_ = i; }
+std::mt19937 randomness;
 
-    int i_;
-    };
-#endif
+template <class Iter>
+void test_one_randomized(unsigned N, unsigned M) {
+  typedef typename std::iterator_traits<Iter>::value_type value_type;
+  value_type* ia = new value_type[N];
+
+  for (unsigned i = 0; i < N; ++i)
+    ia[i] = i;
+  std::shuffle(ia, ia + N, randomness);
+  std::sort(ia, ia + M);
+  std::sort(ia + M, ia + N);
+  std::inplace_merge(Iter(ia), Iter(ia + M), Iter(ia + N));
+  if (N > 0) {
+    assert(ia[0] == 0);
+    assert(ia[N - 1] == static_cast<value_type>(N - 1));
+    assert(std::is_sorted(ia, ia + N));
+  }
+  delete[] ia;
+}
 
-std::mt19937 randomness;
+template <class Iter>
+TEST_CONSTEXPR_CXX26 void test_one_non_randomized(unsigned N, unsigned M) {
+  typedef typename std::iterator_traits<Iter>::value_type value_type;
+  value_type* ia                  = new value_type[N];
+  const unsigned long small_prime = 19937;
+  const unsigned long large_prime = 212987;
+  unsigned long product_mod       = small_prime;
+  for (unsigned i = 0; i < N; ++i) {
+    ia[i]       = static_cast<int>(product_mod);
+    product_mod = product_mod * small_prime % large_prime;
+  }
+  std::sort(ia, ia + M);
+  std::sort(ia + M, ia + N);
+  std::inplace_merge(Iter(ia), Iter(ia + M), Iter(ia + N));
+  if (N > 0) {
+    assert(std::is_sorted(ia, ia + N));
+  }
+  delete[] ia;
+}
 
 template <class Iter>
-void
-test_one(unsigned N, unsigned M)
-{
-    typedef typename std::iterator_traits<Iter>::value_type value_type;
-    assert(M <= N);
-    value_type* ia = new value_type[N];
-    for (unsigned i = 0; i < N; ++i)
-        ia[i] = i;
-    std::shuffle(ia, ia+N, randomness);
-    std::sort(ia, ia+M);
-    std::sort(ia+M, ia+N);
-    std::inplace_merge(Iter(ia), Iter(ia+M), Iter(ia+N));
-    if(N > 0)
-    {
-        assert(ia[0] == 0);
-        assert(ia[N-1] == static_cast<value_type>(N-1));
-        assert(std::is_sorted(ia, ia+N));
-    }
-    delete [] ia;
+TEST_CONSTEXPR_CXX26 void test_one(unsigned N, unsigned M) {
+  assert(M <= N);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    test_one_randomized<Iter>(N, M);
+  }
+  test_one_non_randomized<Iter>(N, M);
 }
 
 template <class Iter>
-void
-test(unsigned N)
-{
-    test_one<Iter>(N, 0);
-    test_one<Iter>(N, N/4);
-    test_one<Iter>(N, N/2);
-    test_one<Iter>(N, 3*N/4);
-    test_one<Iter>(N, N);
+TEST_CONSTEXPR_CXX26 void test(unsigned N) {
+  test_one<Iter>(N, 0);
+  test_one<Iter>(N, N / 4);
+  test_one<Iter>(N, N / 2);
+  test_one<Iter>(N, 3 * N / 4);
+  test_one<Iter>(N, N);
 }
 
 template <class Iter>
-void
-test()
-{
-    test_one<Iter>(0, 0);
-    test_one<Iter>(1, 0);
-    test_one<Iter>(1, 1);
-    test_one<Iter>(2, 0);
-    test_one<Iter>(2, 1);
-    test_one<Iter>(2, 2);
-    test_one<Iter>(3, 0);
-    test_one<Iter>(3, 1);
-    test_one<Iter>(3, 2);
-    test_one<Iter>(3, 3);
-    test<Iter>(4);
-    test<Iter>(100);
+TEST_CONSTEXPR_CXX26 void test() {
+  test_one<Iter>(0, 0);
+  test_one<Iter>(1, 0);
+  test_one<Iter>(1, 1);
+  test_one<Iter>(2, 0);
+  test_one<Iter>(2, 1);
+  test_one<Iter>(2, 2);
+  test_one<Iter>(3, 0);
+  test_one<Iter>(3, 1);
+  test_one<Iter>(3, 2);
+  test_one<Iter>(3, 3);
+  test<Iter>(4);
+  test<Iter>(100);
+  if (!TEST_IS_CONSTANT_EVALUATED) { // avoid blowing past constant evaluation limit
     test<Iter>(1000);
+  }
 }
 
-int main(int, char**)
-{
-    test<bidirectional_iterator<int*> >();
-    test<random_access_iterator<int*> >();
-    test<int*>();
+TEST_CONSTEXPR_CXX26 bool test() {
+  test<bidirectional_iterator<int*> >();
+  test<random_access_iterator<int*> >();
+  test<int*>();
 
 #if TEST_STD_VER >= 11
-    test<bidirectional_iterator<S*> >();
-    test<random_access_iterator<S*> >();
-    test<S*>();
-#endif
+  test<bidirectional_iterator<S*> >();
+  test<random_access_iterator<S*> >();
+  test<S*>();
+#endif // TEST_STD_VER >= 11
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif // TEST_STD_VER >= 26
 
 #if TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS)
-    {
-        std::vector<int> vec(150, 3);
-        getGlobalMemCounter()->throw_after = 0;
-        std::inplace_merge(vec.begin(), vec.begin() + 100, vec.end());
-        assert(std::all_of(vec.begin(), vec.end(), [](int i) { return i == 3; }));
-    }
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    std::vector<int> vec(150, 3);
+    getGlobalMemCounter()->throw_after = 0;
+    std::inplace_merge(vec.begin(), vec.begin() + 100, vec.end());
+    assert(std::all_of(vec.begin(), vec.end(), [](int i) { return i == 3; }));
+  }
 #endif // TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS)
 
   return 0;
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp
index bcde2323ad1de..3c98c8377de21 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp
@@ -8,11 +8,9 @@
 
 // <algorithm>
 
-// template<BidirectionalIterator Iter, StrictWeakOrder<auto, Iter::value_type> Compare>
-//   requires ShuffleIterator<Iter>
-//         && CopyConstructible<Compare>
-//   void
-//   inplace_merge(Iter first, Iter middle, Iter last, Compare comp);
+// template<class BidirectionalIterator, class Compare>
+//   constexpr void                                         // constexpr since C++26
+//   inplace_merge(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last, Compare comp);
 
 #include <algorithm>
 #include <cassert>
@@ -23,37 +21,46 @@
 #include "test_macros.h"
 
 #if TEST_STD_VER >= 11
-#include <memory>
+#  include <memory>
 
 struct indirect_less {
   template <class P>
-  bool operator()(const P& x, const P& y) const {
+  TEST_CONSTEXPR_CXX26 bool operator()(const P& x, const P& y) const {
     return *x < *y;
   }
 };
 
 struct S {
-    S() : i_(0) {}
-    S(int i) : i_(i) {}
+  TEST_CONSTEXPR_CXX26 S() : i_(0) {}
+  TEST_CONSTEXPR_CXX26 S(int i) : i_(i) {}
 
-    S(const S&  rhs) : i_(rhs.i_) {}
-    S(      S&& rhs) : i_(rhs.i_) { rhs.i_ = -1; }
+  TEST_CONSTEXPR_CXX26 S(const S& rhs) : i_(rhs.i_) {}
+  TEST_CONSTEXPR_CXX26 S(S&& rhs) : i_(rhs.i_) { rhs.i_ = -1; }
 
-    S& operator =(const S&  rhs) { i_ = rhs.i_;              return *this; }
-    S& operator =(      S&& rhs) { i_ = rhs.i_; rhs.i_ = -2; assert(this != &rhs); return *this; }
-    S& operator =(int i)         { i_ = i;                   return *this; }
-
-    bool operator  <(const S&  rhs) const { return i_ < rhs.i_; }
-    bool operator  >(const S&  rhs) const { return i_ > rhs.i_; }
-    bool operator ==(const S&  rhs) const { return i_ == rhs.i_; }
-    bool operator ==(int i)         const { return i_ == i; }
-
-    void set(int i) { i_ = i; }
+  TEST_CONSTEXPR_CXX26 S& operator=(const S& rhs) {
+    i_ = rhs.i_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX26 S& operator=(S&& rhs) {
+    i_     = rhs.i_;
+    rhs.i_ = -2;
+    assert(this != &rhs);
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX26 S& operator=(int i) {
+    i_ = i;
+    return *this;
+  }
 
-    int i_;
-    };
+  TEST_CONSTEXPR_CXX26 bool operator<(const S& rhs) const { return i_ < rhs.i_; }
+  TEST_CONSTEXPR_CXX26 bool operator>(const S& rhs) const { return i_ > rhs.i_; }
+  TEST_CONSTEXPR_CXX26 bool operator==(const S& rhs) const { return i_ == rhs.i_; }
+  TEST_CONSTEXPR_CXX26 bool operator==(int i) const { return i_ == i; }
 
+  TEST_CONSTEXPR_CXX26 void set(int i) { i_ = i; }
 
+  int i_;
+};
 #endif // TEST_STD_VER >= 11
 
 #include "test_iterators.h"
@@ -62,114 +69,173 @@ struct S {
 std::mt19937 randomness;
 
 template <class Iter>
-void
-test_one(unsigned N, unsigned M)
-{
-    assert(M <= N);
-    typedef typename std::iterator_traits<Iter>::value_type value_type;
-    value_type* ia = new value_type[N];
-    for (unsigned i = 0; i < N; ++i)
-        ia[i] = i;
-    std::shuffle(ia, ia+N, randomness);
-    std::sort(ia, ia+M, std::greater<value_type>());
-    std::sort(ia+M, ia+N, std::greater<value_type>());
-    binary_counting_predicate<std::greater<value_type>, value_type, value_type> pred((std::greater<value_type>()));
-    std::inplace_merge(Iter(ia), Iter(ia+M), Iter(ia+N), std::ref(pred));
-    if(N > 0)
-    {
-        assert(ia[0] == static_cast<int>(N)-1);
-        assert(ia[N-1] == 0);
-        assert(std::is_sorted(ia, ia+N, std::greater<value_type>()));
+void test_one_randomized(unsigned N, unsigned M) {
+  typedef typename std::iterator_traits<Iter>::value_type value_type;
+
+  value_type* ia = new value_type[N];
+  for (unsigned i = 0; i < N; ++i)
+    ia[i] = i;
+  std::shuffle(ia, ia + N, randomness);
+  std::sort(ia, ia + M, std::greater<value_type>());
+  std::sort(ia + M, ia + N, std::greater<value_type>());
+  binary_counting_predicate<std::greater<value_type>, value_type, value_type> pred((std::greater<value_type>()));
+  std::inplace_merge(Iter(ia), Iter(ia + M), Iter(ia + N), std::ref(pred));
+  if (N > 0) {
+    assert(ia[0] == static_cast<int>(N) - 1);
+    assert(ia[N - 1] == 0);
+    assert(std::is_sorted(ia, ia + N, std::greater<value_type>()));
+#if defined(_LIBCPP_HARDENING_MODE) && _LIBCPP_HARDENING_MODE != _LIBCPP_HARDENING_MODE_DEBUG
+    assert(pred.count() <= (N - 1));
+#endif
+  }
+  delete[] ia;
+}
+
+template <class Iter>
+TEST_CONSTEXPR_CXX26 void test_one_non_randomized(unsigned N, unsigned M) {
+  typedef typename std::iterator_traits<Iter>::value_type value_type;
+
+  value_type* ia                  = new value_type[N];
+  const unsigned long small_prime = 19937;
+  const unsigned long large_prime = 212987;
+  unsigned long product_mod       = small_prime;
+  for (unsigned i = 0; i < N; ++i) {
+    ia[i]       = static_cast<int>(product_mod);
+    product_mod = product_mod * small_prime % large_prime;
+  }
+  std::sort(ia, ia + M, std::greater<value_type>());
+  std::sort(ia + M, ia + N, std::greater<value_type>());
+  binary_counting_predicate<std::greater<value_type>, value_type, value_type> pred((std::greater<value_type>()));
+  std::inplace_merge(Iter(ia), Iter(ia + M), Iter(ia + N), std::ref(pred));
+  if (N > 0) {
+    assert(std::is_sorted(ia, ia + N, std::greater<value_type>()));
 #if defined(_LIBCPP_HARDENING_MODE) && _LIBCPP_HARDENING_MODE != _LIBCPP_HARDENING_MODE_DEBUG
-        assert(pred.count() <= (N-1));
+    assert(pred.count() <= (N - 1));
 #endif
-    }
-    delete [] ia;
+  }
+  delete[] ia;
+}
+
+template <class Iter>
+TEST_CONSTEXPR_CXX26 void test_one(unsigned N, unsigned M) {
+  assert(M <= N);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    test_one_randomized<Iter>(N, M);
+  }
+  test_one_non_randomized<Iter>(N, M);
 }
 
 template <class Iter>
-void
-test(unsigned N)
-{
-    test_one<Iter>(N, 0);
-    test_one<Iter>(N, N/4);
-    test_one<Iter>(N, N/2);
-    test_one<Iter>(N, 3*N/4);
-    test_one<Iter>(N, N);
+TEST_CONSTEXPR_CXX26 void test(unsigned N) {
+  test_one<Iter>(N, 0);
+  test_one<Iter>(N, N / 4);
+  test_one<Iter>(N, N / 2);
+  test_one<Iter>(N, 3 * N / 4);
+  test_one<Iter>(N, N);
 }
 
 template <class Iter>
-void
-test()
-{
-    test_one<Iter>(0, 0);
-    test_one<Iter>(1, 0);
-    test_one<Iter>(1, 1);
-    test_one<Iter>(2, 0);
-    test_one<Iter>(2, 1);
-    test_one<Iter>(2, 2);
-    test_one<Iter>(3, 0);
-    test_one<Iter>(3, 1);
-    test_one<Iter>(3, 2);
-    test_one<Iter>(3, 3);
-    test<Iter>(4);
-    test<Iter>(20);
-    test<Iter>(100);
+TEST_CONSTEXPR_CXX26 void test() {
+  test_one<Iter>(0, 0);
+  test_one<Iter>(1, 0);
+  test_one<Iter>(1, 1);
+  test_one<Iter>(2, 0);
+  test_one<Iter>(2, 1);
+  test_one<Iter>(2, 2);
+  test_one<Iter>(3, 0);
+  test_one<Iter>(3, 1);
+  test_one<Iter>(3, 2);
+  test_one<Iter>(3, 3);
+  test<Iter>(4);
+  test<Iter>(20);
+  test<Iter>(100);
+  if (!TEST_IS_CONSTANT_EVALUATED) { // avoid blowing past constant evaluation limit
     test<Iter>(1000);
+  }
 }
 
 struct less_by_first {
   template <typename Pair>
-  bool operator()(const Pair& lhs, const Pair& rhs) const {
+  TEST_CONSTEXPR_CXX26 bool operator()(const Pair& lhs, const Pair& rhs) const {
     return std::less<typename Pair::first_type>()(lhs.first, rhs.first);
   }
 };
 
-void test_PR31166 ()
-{
-    typedef std::pair<int, int> P;
-    typedef std::vector<P> V;
-    P vec[5] = {P(1, 0), P(2, 0), P(2, 1), P(2, 2), P(2, 3)};
-    for ( int i = 0; i < 5; ++i ) {
-        V res(vec, vec + 5);
-        std::inplace_merge(res.begin(), res.begin() + i, res.end(), less_by_first());
-        assert(res.size() == 5);
-        assert(std::equal(res.begin(), res.end(), vec));
-    }
+TEST_CONSTEXPR_CXX26 void test_PR31166() {
+  typedef std::pair<int, int> P;
+  typedef std::vector<P> V;
+  P vec[5] = {P(1, 0), P(2, 0), P(2, 1), P(2, 2), P(2, 3)};
+  for (int i = 0; i < 5; ++i) {
+    V res(vec, vec + 5);
+    std::inplace_merge(res.begin(), res.begin() + i, res.end(), less_by_first());
+    assert(res.size() == 5);
+    assert(std::equal(res.begin(), res.end(), vec));
+  }
 }
 
-int main(int, char**)
-{
-    test<bidirectional_iterator<int*> >();
-    test<random_access_iterator<int*> >();
-    test<int*>();
+#if TEST_STD_VER >= 11
+void test_wrapped_randomized(int N, unsigned M) {
+  std::unique_ptr<int>* ia = new std::unique_ptr<int>[N];
+  for (int i = 0; i < N; ++i)
+    ia[i].reset(new int(i));
+  std::shuffle(ia, ia + N, randomness);
+  std::sort(ia, ia + M, indirect_less());
+  std::sort(ia + M, ia + N, indirect_less());
+  std::inplace_merge(ia, ia + M, ia + N, indirect_less());
+  if (N > 0) {
+    assert(*ia[0] == 0);
+    assert(*ia[N - 1] == N - 1);
+    assert(std::is_sorted(ia, ia + N, indirect_less()));
+  }
+  delete[] ia;
+}
+
+TEST_CONSTEXPR_CXX26 void test_wrapped_non_randomized(int N, unsigned M) {
+  std::unique_ptr<int>* ia = new std::unique_ptr<int>[N];
+
+  const unsigned long small_prime = 19937;
+  const unsigned long large_prime = 212987;
+  unsigned long product_mod       = small_prime;
+  for (unsigned i = 0; i < N; ++i) {
+    ia[i].reset(new int(static_cast<int>(product_mod)));
+    product_mod = product_mod * small_prime % large_prime;
+  }
+  std::sort(ia, ia + M, indirect_less());
+  std::sort(ia + M, ia + N, indirect_less());
+  std::inplace_merge(ia, ia + M, ia + N, indirect_less());
+  if (N > 0) {
+    assert(std::is_sorted(ia, ia + N, indirect_less()));
+  }
+  delete[] ia;
+}
+#endif // TEST_STD_VER >= 11
+
+TEST_CONSTEXPR_CXX26 bool test() {
+  test<bidirectional_iterator<int*> >();
+  test<random_access_iterator<int*> >();
+  test<int*>();
 
 #if TEST_STD_VER >= 11
-    test<bidirectional_iterator<S*> >();
-    test<random_access_iterator<S*> >();
-    test<S*>();
-
-    {
-    int N = 100;
-    unsigned M = 50;
-    std::unique_ptr<int>* ia = new std::unique_ptr<int>[N];
-    for (int i = 0; i < N; ++i)
-        ia[i].reset(new int(i));
-    std::shuffle(ia, ia+N, randomness);
-    std::sort(ia, ia+M, indirect_less());
-    std::sort(ia+M, ia+N, indirect_less());
-    std::inplace_merge(ia, ia+M, ia+N, indirect_less());
-    if(N > 0)
-    {
-        assert(*ia[0] == 0);
-        assert(*ia[N-1] == N-1);
-        assert(std::is_sorted(ia, ia+N, indirect_less()));
-    }
-    delete [] ia;
-    }
+  test<bidirectional_iterator<S*> >();
+  test<random_access_iterator<S*> >();
+  test<S*>();
+
+  if (!TEST_IS_CONSTANT_EVALUATED)...
[truncated]

@frederick-vs-ja frederick-vs-ja force-pushed the constexpr-std-inplace_merge branch from 129af09 to 76b5ef4 Compare March 2, 2025 11:45
@mordante mordante self-assigned this Mar 2, 2025
Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! Some comments regarding the tests.

Copy link

github-actions bot commented Mar 10, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a few minor issues, otherwise LGTM!

- Skip 100 cases for constant evaluation.
- Cite the source of MS UCRT's constants.
Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@frederick-vs-ja frederick-vs-ja merged commit 854a4f2 into llvm:main Mar 18, 2025
85 checks passed
@frederick-vs-ja frederick-vs-ja deleted the constexpr-std-inplace_merge branch March 18, 2025 23:42
test<random_access_iterator<int*> >();
test<int*>();
TEST_CONSTEXPR_CXX26 bool test() {
support::simple_random_generator randomness;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I think I would have favoured an approach where we get rid of the randomness in these tests instead, but I think this is acceptable and certainly has benefits. No action needed.

frederick-vs-ja pushed a commit that referenced this pull request Mar 20, 2025
* libcxx/test/support/min_allocator.h
+ Fix `tiny_size_allocator::rebind` which mistakenly said `T` instead of
`U`.
*
libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.pass.cpp
  + `std::stable_partition` requires bidirectional iterators.
* libcxx/test/std/containers/sequences/vector.bool/max_size.pass.cpp
+ Fix allocator type given to `std::vector<bool>`. The element types are
required to match, [N5008](https://isocpp.org/files/papers/N5008.pdf)
\[container.alloc.reqmts\]/5: "*Mandates:* `allocator_type::value_type`
is the same as `X::value_type`."
* libcxx/test/std/time/time.clock/time.clock.utc/types.compile.pass.cpp
+ Mark `is_steady` as `[[maybe_unused]]`, as it appears within
`LIBCPP_STATIC_ASSERT` only.
*
libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/rotate.pass.cpp
*
libcxx/test/std/algorithms/alg.modifying.operations/alg.swap/swap_ranges.pass.cpp
* libcxx/test/std/utilities/utility/utility.swap/swap_array.pass.cpp
+ Fix MSVC warning C4127 "conditional expression is constant".
`TEST_STD_AT_LEAST_23_OR_RUNTIME_EVALUATED` was introduced for this
purpose, so it should be used consistently.
* libcxx/test/std/numerics/numeric.ops/numeric.ops.gcd/gcd.pass.cpp
+ Fix `gcd()` precondition violation for `signed char`. This test case
was causing `-128` to be passed as a `signed char` to `gcd()`, which is
forbidden.
* libcxx/test/std/containers/sequences/array/assert.iterators.pass.cpp
*
libcxx/test/std/containers/sequences/vector/vector.modifiers/assert.push_back.invalidation.pass.cpp
*
libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+ Split some REQUIRES and XFAIL lines. This is a "nice to have" for
MSVC's internal test harness, which is extremely simple and looks for
exact comment matches to skip tests. We can recognize the specific lines
"REQUIRES: has-unix-headers" and "XFAIL: msvc", but it's a headache to
maintain if they're chained with other conditions.
* libcxx/test/support/sized_allocator.h
+ Fix x86 truncation warnings. `std::allocator` takes `std::size_t`, so
we need to `static_cast`.
*
libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/offset_range.pass.cpp
+ Fix x86 truncation warning. `std::min()` is returning
`std::streamoff`, which was being unnecessarily narrowed to
`std::size_t`.
*
libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge_comp.pass.cpp
+ Fix MSVC warning C4127 "conditional expression is constant" for an
always-true branch. This was very recently introduced by #129008 making
`N` constexpr. As it's a local constant just nine lines above, we don't
need to test whether 100 is greater than 0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc++] P2562R1: inplace_merge
4 participants