[libcxx-commits] [libcxx] d406c64 - [libc++][ranges] Implement `ranges::is_heap{, _until}`.

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jul 26 16:11:34 PDT 2022


Author: Konstantin Varlamov
Date: 2022-07-26T16:11:24-07:00
New Revision: d406c6493e9e0e6334e16e8fd22b0ae613653704

URL: https://github.com/llvm/llvm-project/commit/d406c6493e9e0e6334e16e8fd22b0ae613653704
DIFF: https://github.com/llvm/llvm-project/commit/d406c6493e9e0e6334e16e8fd22b0ae613653704.diff

LOG: [libc++][ranges] Implement `ranges::is_heap{,_until}`.

Differential Revision: https://reviews.llvm.org/D130547

Added: 
    

Modified: 
    libcxx/docs/Status/RangesAlgorithms.csv
    libcxx/include/__algorithm/is_heap.h
    libcxx/include/__algorithm/is_heap_until.h
    libcxx/include/__algorithm/ranges_is_heap.h
    libcxx/include/__algorithm/ranges_is_heap_until.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.heap.operations/is.heap/ranges_is_heap.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
    libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
    libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.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/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index 35db0ad64ae81..ecbfb3329c536 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -30,8 +30,8 @@ Read-only,is_partitioned,Nikolas Klauser,`D124440 <https://llvm.org/D124440>`_,
 Read-only,is_sorted,Nikolas Klauser,`D125608 <https://llvm.org/D125608>`_,✅
 Read-only,is_sorted_until,Nikolas Klauser,`D125608 <https://llvm.org/D125608>`_,✅
 Read-only,includes,Hui Xie,`D130116 <https://llvm.org/D130116>`,✅
-Read-only,is_heap,Nikolas Klauser,n/a,Not started
-Read-only,is_heap_until,Nikolas Klauser,n/a,Not started
+Read-only,is_heap,Konstantin Varlamov,`D130547 <https://llvm.org/D130547>`_,✅
+Read-only,is_heap_until,Konstantin Varlamov,`D130547 <https://llvm.org/D130547>`_,✅
 Read-only,clamp,Nikolas Klauser,`D126193 <https://llvm.org/D126193>`_,Under review
 Read-only,is_permutation,Nikolas Klauser,`D127194 <https://llvm.org/D127194>`_,Under review
 Read-only,for_each,Nikolas Klauser,`D124332 <https://llvm.org/D124332>`_,✅

diff  --git a/libcxx/include/__algorithm/is_heap.h b/libcxx/include/__algorithm/is_heap.h
index fe44e634f6ddd..0d399a09b8576 100644
--- a/libcxx/include/__algorithm/is_heap.h
+++ b/libcxx/include/__algorithm/is_heap.h
@@ -28,7 +28,7 @@ bool
 is_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
 {
     typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
-    return _VSTD::__is_heap_until<_Comp_ref>(__first, __last, __comp) == __last;
+    return std::__is_heap_until(__first, __last, static_cast<_Comp_ref>(__comp)) == __last;
 }
 
 template<class _RandomAccessIterator>

diff  --git a/libcxx/include/__algorithm/is_heap_until.h b/libcxx/include/__algorithm/is_heap_until.h
index 39f313eb0d3fc..adb35af887fba 100644
--- a/libcxx/include/__algorithm/is_heap_until.h
+++ b/libcxx/include/__algorithm/is_heap_until.h
@@ -22,7 +22,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Compare, class _RandomAccessIterator>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _RandomAccessIterator
-__is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
+__is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp)
 {
     typedef typename iterator_traits<_RandomAccessIterator>::
diff erence_type 
diff erence_type;
     
diff erence_type __len = __last - __first;
@@ -52,7 +52,7 @@ _LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
 is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
 {
     typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
-    return _VSTD::__is_heap_until<_Comp_ref>(__first, __last, __comp);
+    return std::__is_heap_until(__first, __last, static_cast<_Comp_ref>(__comp));
 }
 
 template<class _RandomAccessIterator>

diff  --git a/libcxx/include/__algorithm/ranges_is_heap.h b/libcxx/include/__algorithm/ranges_is_heap.h
index 00105189fed7d..a3e86d1a8d724 100644
--- a/libcxx/include/__algorithm/ranges_is_heap.h
+++ b/libcxx/include/__algorithm/ranges_is_heap.h
@@ -9,18 +9,17 @@
 #ifndef _LIBCPP___ALGORITHM_RANGES_IS_HEAP_H
 #define _LIBCPP___ALGORITHM_RANGES_IS_HEAP_H
 
-#include <__algorithm/is_heap.h>
+#include <__algorithm/is_heap_until.h>
 #include <__algorithm/make_projected.h>
 #include <__config>
 #include <__functional/identity.h>
-#include <__functional/invoke.h>
 #include <__functional/ranges_operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
 #include <__iterator/projected.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
-#include <__utility/forward.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -36,22 +35,28 @@ namespace __is_heap {
 
 struct __fn {
 
+  template <class _Iter, class _Sent, class _Proj, class _Comp>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  static bool __is_heap_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
+    auto __last_iter = ranges::next(__first, __last);
+    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+
+    auto __result = std::__is_heap_until(std::move(__first), std::move(__last_iter), __projected_comp);
+    return __result == __last;
+  }
+
   template <random_access_iterator _Iter, sentinel_for<_Iter> _Sent, class _Proj = identity,
             indirect_strict_weak_order<projected<_Iter, _Proj>> _Comp = ranges::less>
   _LIBCPP_HIDE_FROM_ABI constexpr
   bool operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
-    // TODO: implement
-    (void)__first; (void)__last; (void)__comp; (void)__proj;
-    return {};
+    return __is_heap_fn_impl(std::move(__first), std::move(__last), __comp, __proj);
   }
 
   template <random_access_range _Range, class _Proj = identity,
             indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>> _Comp = ranges::less>
   _LIBCPP_HIDE_FROM_ABI constexpr
   bool operator()(_Range&& __range, _Comp __comp = {}, _Proj __proj = {}) const {
-    // TODO: implement
-    (void)__range; (void)__comp; (void)__proj;
-    return {};
+    return __is_heap_fn_impl(ranges::begin(__range), ranges::end(__range), __comp, __proj);
   }
 };
 

diff  --git a/libcxx/include/__algorithm/ranges_is_heap_until.h b/libcxx/include/__algorithm/ranges_is_heap_until.h
index ad021d6f2525e..bcd33ad404e88 100644
--- a/libcxx/include/__algorithm/ranges_is_heap_until.h
+++ b/libcxx/include/__algorithm/ranges_is_heap_until.h
@@ -13,15 +13,14 @@
 #include <__algorithm/make_projected.h>
 #include <__config>
 #include <__functional/identity.h>
-#include <__functional/invoke.h>
 #include <__functional/ranges_operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
 #include <__iterator/projected.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
 #include <__ranges/dangling.h>
-#include <__utility/forward.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -37,22 +36,27 @@ namespace __is_heap_until {
 
 struct __fn {
 
+  template <class _Iter, class _Sent, class _Proj, class _Comp>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  static _Iter __is_heap_until_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
+    auto __last_iter = ranges::next(__first, __last);
+    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+
+    return std::__is_heap_until(std::move(__first), std::move(__last_iter), __projected_comp);
+  }
+
   template <random_access_iterator _Iter, sentinel_for<_Iter> _Sent, class _Proj = identity,
             indirect_strict_weak_order<projected<_Iter, _Proj>> _Comp = ranges::less>
   _LIBCPP_HIDE_FROM_ABI constexpr
   _Iter operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
-    // TODO: implement
-    (void)__first; (void)__last; (void)__comp; (void)__proj;
-    return {};
+    return __is_heap_until_fn_impl(std::move(__first), std::move(__last), __comp, __proj);
   }
 
   template <random_access_range _Range, class _Proj = identity,
             indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>> _Comp = ranges::less>
   _LIBCPP_HIDE_FROM_ABI constexpr
   borrowed_iterator_t<_Range> operator()(_Range&& __range, _Comp __comp = {}, _Proj __proj = {}) const {
-    // TODO: implement
-    (void)__range; (void)__comp; (void)__proj;
-    return {};
+    return __is_heap_until_fn_impl(ranges::begin(__range), ranges::end(__range), __comp, __proj);
   }
 
 };

diff  --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 30b04e155bac0..197b3b1043bbe 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -331,6 +331,23 @@ namespace ranges {
     constexpr borrowed_iterator_t<R>
       ranges::sort_heap(R&& r, Comp comp = {}, Proj proj = {});                             // since C++20
 
+  template<random_access_iterator I, sentinel_for<I> S, class Proj = identity,
+            indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
+    constexpr bool is_heap(I first, S last, Comp comp = {}, Proj proj = {});                // Since C++20
+
+  template<random_access_range R, class Proj = identity,
+            indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
+    constexpr bool is_heap(R&& r, Comp comp = {}, Proj proj = {});                          // Since C++20
+
+  template<random_access_iterator I, sentinel_for<I> S, class Proj = identity,
+           indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
+    constexpr I is_heap_until(I first, S last, Comp comp = {}, Proj proj = {});             // Since C++20
+
+  template<random_access_range R, class Proj = identity,
+           indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
+    constexpr borrowed_iterator_t<R>
+      is_heap_until(R&& r, Comp comp = {}, Proj proj = {});                                 // Since C++20
+
   template<bidirectional_iterator I, sentinel_for<I> S>
     requires permutable<I>
     constexpr I ranges::reverse(I first, S last);                                           // since C++20
@@ -1590,6 +1607,8 @@ template <class BidirectionalIterator, class Compare>
 #include <__algorithm/ranges_generate.h>
 #include <__algorithm/ranges_generate_n.h>
 #include <__algorithm/ranges_includes.h>
+#include <__algorithm/ranges_is_heap.h>
+#include <__algorithm/ranges_is_heap_until.h>
 #include <__algorithm/ranges_is_partitioned.h>
 #include <__algorithm/ranges_is_sorted.h>
 #include <__algorithm/ranges_is_sorted_until.h>

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 6fe0cfc177ef6..48eeb01b57587 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
@@ -126,10 +126,10 @@ constexpr bool all_the_algorithms()
     //(void)std::ranges::generate_n(first, count, NullaryValue(&copies)); assert(copies == 0);
     (void)std::ranges::includes(first, last, first2, last2, Less(&copies)); assert(copies == 0);
     (void)std::ranges::includes(a, b, Less(&copies)); assert(copies == 0);
-    //(void)std::ranges::is_heap(first, last, Less(&copies)); assert(copies == 0);
-    //(void)std::ranges::is_heap(a, Less(&copies)); assert(copies == 0);
-    //(void)std::ranges::is_heap_until(first, last, Less(&copies)); assert(copies == 0);
-    //(void)std::ranges::is_heap_until(a, Less(&copies)); assert(copies == 0);
+    (void)std::ranges::is_heap(first, last, Less(&copies)); assert(copies == 0);
+    (void)std::ranges::is_heap(a, Less(&copies)); assert(copies == 0);
+    (void)std::ranges::is_heap_until(first, last, Less(&copies)); assert(copies == 0);
+    (void)std::ranges::is_heap_until(a, Less(&copies)); assert(copies == 0);
     (void)std::ranges::is_partitioned(first, last, UnaryTrue(&copies)); assert(copies == 0);
     (void)std::ranges::is_partitioned(a, UnaryTrue(&copies)); assert(copies == 0);
     //(void)std::ranges::is_permutation(first, last, first2, last2, 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 56e7471c73f98..80b8a5ec4d836 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
@@ -109,10 +109,10 @@ constexpr bool all_the_algorithms()
     (void)std::ranges::for_each_n(first, count, UnaryVoid(), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::includes(first, last, first2, last2, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::includes(a, b, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
-    //(void)std::ranges::is_heap(first, last, Less(), Proj(&copies)); assert(copies == 0);
-    //(void)std::ranges::is_heap(a, Less(), Proj(&copies)); assert(copies == 0);
-    //(void)std::ranges::is_heap_until(first, last, Less(), Proj(&copies)); assert(copies == 0);
-    //(void)std::ranges::is_heap_until(a, Less(), Proj(&copies)); assert(copies == 0);
+    (void)std::ranges::is_heap(first, last, Less(), Proj(&copies)); assert(copies == 0);
+    (void)std::ranges::is_heap(a, Less(), Proj(&copies)); assert(copies == 0);
+    (void)std::ranges::is_heap_until(first, last, Less(), Proj(&copies)); assert(copies == 0);
+    (void)std::ranges::is_heap_until(a, Less(), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::is_partitioned(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::is_partitioned(a, UnaryTrue(), Proj(&copies)); assert(copies == 0);
     //(void)std::ranges::is_permutation(first, last, first2, last2, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap.pass.cpp
index 28b1c4af4ee91..8d58009a70fd5 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap.pass.cpp
@@ -12,28 +12,158 @@
 // <algorithm>
 
 // template<random_access_iterator I, sentinel_for<I> S, class Proj = identity,
-//              indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
-//       constexpr bool is_heap(I first, S last, Comp comp = {}, Proj proj = {});                   // Since C++20
+//          indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
+//   constexpr bool is_heap(I first, S last, Comp comp = {}, Proj proj = {});                   // Since C++20
 //
-//     template<random_access_range R, class Proj = identity,
-//              indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
-//       constexpr bool is_heap(R&& r, Comp comp = {}, Proj proj = {});                             // Since C++20
+// template<random_access_range R, class Proj = identity,
+//          indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
+//   constexpr bool is_heap(R&& r, Comp comp = {}, Proj proj = {});                             // Since C++20
 
 #include <algorithm>
 #include <array>
 #include <concepts>
 #include <functional>
 #include <ranges>
+#include <utility>
 
 #include "almost_satisfies_types.h"
 #include "test_iterators.h"
 
-// TODO: SFINAE tests.
+// Test constraints of the (iterator, sentinel) overload.
+// ======================================================
+
+template <class Iter = int*, class Sent = int*, class Comp = std::ranges::less>
+concept HasIsHeapIter =
+    requires(Iter&& iter, Sent&& sent, Comp&& comp) {
+      std::ranges::is_heap(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Comp>(comp));
+    };
+
+static_assert(HasIsHeapIter<int*, int*, std::ranges::less>);
+
+// !random_access_iterator<I>
+static_assert(!HasIsHeapIter<RandomAccessIteratorNotDerivedFrom>);
+static_assert(!HasIsHeapIter<RandomAccessIteratorBadIndex>);
+
+// !sentinel_for<S, I>
+static_assert(!HasIsHeapIter<int*, SentinelForNotSemiregular>);
+static_assert(!HasIsHeapIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+struct NoComparator {};
+// !indirect_strict_weak_order<Comp, projected<I, Proj>>
+static_assert(!HasIsHeapIter<NoComparator*, NoComparator*>);
+
+// Test constraints of the (range) overload.
+// =========================================
+
+template <class Range, class Comp = std::ranges::less>
+concept HasIsHeapRange =
+    requires(Range&& range, Comp&& comp) {
+      std::ranges::is_heap(std::forward<Range>(range), std::forward<Comp>(comp));
+    };
+
+template <class T>
+using R = UncheckedRange<T>;
+
+static_assert(HasIsHeapRange<R<int*>>);
+
+// !random_access_range<R>
+static_assert(!HasIsHeapRange<RandomAccessRangeNotDerivedFrom>);
+static_assert(!HasIsHeapRange<RandomAccessRangeBadIndex>);
+
+// !indirect_strict_weak_order<Comp, projected<iterator_t<R>, Proj>>
+static_assert(!HasIsHeapRange<R<NoComparator*>>);
+
+template <class Iter, class Sent, size_t N>
+constexpr void test_one(std::array<int, N> input, bool expected) {
+  auto begin = Iter(input.data());
+  auto end = Sent(Iter(input.data() + input.size()));
+
+  { // (iterator, sentinel) overload.
+    std::same_as<bool> decltype(auto) result = std::ranges::is_heap(begin, end);
+    assert(result == expected);
+  }
+
+  { // (range) overload.
+    auto range = std::ranges::subrange(begin, end);
+    std::same_as<bool> decltype(auto) result = std::ranges::is_heap(range);
+    assert(result == expected);
+  }
+}
+
+template <class Iter, class Sent>
+constexpr void test_iter_sent() {
+  // Empty sequence.
+  test_one<Iter, Sent, 0>({}, true);
+  // 1-element sequence.
+  test_one<Iter, Sent>(std::array{1}, true);
+  // 2-element sequence, a heap.
+  test_one<Iter, Sent>(std::array{2, 1}, true);
+  // 2-element sequence, not a heap.
+  test_one<Iter, Sent>(std::array{1, 2}, false);
+  // Longer sequence, a heap.
+  test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 5, 2}, true);
+  // Longer sequence, not a heap.
+  test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 2, 5}, false);
+  // Longer sequence with duplicates, a heap.
+  test_one<Iter, Sent>(std::array{8, 7, 5, 5, 6, 4, 1, 2, 3, 2}, true);
+  // Longer sequence with duplicates, not a heap.
+  test_one<Iter, Sent>(std::array{7, 5, 5, 6, 4, 1, 2, 3, 2, 8}, false);
+  // All elements are the same.
+  test_one<Iter, Sent>(std::array{1, 1, 1}, true);
+}
+
+template <class Iter>
+constexpr void test_iter() {
+  test_iter_sent<Iter, Iter>();
+  test_iter_sent<Iter, sentinel_wrapper<Iter>>();
+}
+
+constexpr void test_iterators() {
+  test_iter<random_access_iterator<int*>>();
+  test_iter<contiguous_iterator<int*>>();
+  test_iter<int*>();
+  test_iter<const int*>();
+}
 
 constexpr bool test() {
-  // TODO: main tests.
-  // TODO: A custom comparator works.
-  // TODO: A custom projection works.
+  test_iterators();
+
+  { // A custom comparator works.
+    std::ranges::less ls;
+    std::ranges::greater gt;
+    std::array in = {1, 3, 2, 5, 4, 7, 8, 6};
+
+    { // (iterator, sentinel) overload.
+      assert(!std::ranges::is_heap(in.begin(), in.end(), ls));
+      assert(std::ranges::is_heap(in.begin(), in.end(), gt));
+    }
+
+    { // (range) overload.
+      assert(!std::ranges::is_heap(in, ls));
+      assert(std::ranges::is_heap(in, gt));
+    }
+  }
+
+  { // A custom projection works.
+    struct A {
+      int x;
+      constexpr auto operator<=>(const A&) const = default;
+    };
+
+    std::array in = {A{-8}, A{-6}, A{-7}, A{-3}, A{-4}, A{-1}, A{-5}, A{-2}};
+    auto negate = [](A a) { return a.x * -1; };
+
+    { // (iterator, sentinel) overload.
+      assert(!std::ranges::is_heap(in.begin(), in.end(), {}));
+      assert(std::ranges::is_heap(in.begin(), in.end(), {}, negate));
+    }
+
+    { // (range) overload.
+      assert(!std::ranges::is_heap(in, {}));
+      assert(std::ranges::is_heap(in, {}, negate));
+    }
+  }
+
 
   return true;
 }

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
index 86da5ab54653d..8ce43657af30e 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
@@ -12,13 +12,13 @@
 // <algorithm>
 
 // template<random_access_iterator I, sentinel_for<I> S, class Proj = identity,
-//              indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
-//       constexpr I is_heap_until(I first, S last, Comp comp = {}, Proj proj = {});                // Since C++20
+//          indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
+//   constexpr I is_heap_until(I first, S last, Comp comp = {}, Proj proj = {});                // Since C++20
 //
-//     template<random_access_range R, class Proj = identity,
-//              indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
-//       constexpr borrowed_iterator_t<R>
-//         is_heap_until(R&& r, Comp comp = {}, Proj proj = {});                                    // Since C++20
+// template<random_access_range R, class Proj = identity,
+//          indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
+//   constexpr borrowed_iterator_t<R>
+//     is_heap_until(R&& r, Comp comp = {}, Proj proj = {});                                    // Since C++20
 
 #include <algorithm>
 #include <array>
@@ -29,12 +29,149 @@
 #include "almost_satisfies_types.h"
 #include "test_iterators.h"
 
-// TODO: SFINAE tests.
+// Test constraints of the (iterator, sentinel) overload.
+// ======================================================
+
+template <class Iter = int*, class Sent = int*, class Comp = std::ranges::less>
+concept HasIsHeapUntilIter =
+    requires(Iter&& iter, Sent&& sent, Comp&& comp) {
+      std::ranges::is_heap_until(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Comp>(comp));
+    };
+
+static_assert(HasIsHeapUntilIter<int*, int*, std::ranges::less>);
+
+// !random_access_iterator<I>
+static_assert(!HasIsHeapUntilIter<RandomAccessIteratorNotDerivedFrom>);
+static_assert(!HasIsHeapUntilIter<RandomAccessIteratorBadIndex>);
+
+// !sentinel_for<S, I>
+static_assert(!HasIsHeapUntilIter<int*, SentinelForNotSemiregular>);
+static_assert(!HasIsHeapUntilIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+struct NoComparator {};
+// !indirect_strict_weak_order<Comp, projected<I, Proj>>
+static_assert(!HasIsHeapUntilIter<NoComparator*, NoComparator*>);
+
+// Test constraints of the (range) overload.
+// =========================================
+
+template <class Range, class Comp = std::ranges::less>
+concept HasIsHeapUntilRange =
+    requires(Range&& range, Comp&& comp) {
+      std::ranges::is_heap_until(std::forward<Range>(range), std::forward<Comp>(comp));
+    };
+
+template <class T>
+using R = UncheckedRange<T>;
+
+static_assert(HasIsHeapUntilRange<R<int*>>);
+
+// !random_access_range<R>
+static_assert(!HasIsHeapUntilRange<RandomAccessRangeNotDerivedFrom>);
+static_assert(!HasIsHeapUntilRange<RandomAccessRangeBadIndex>);
+
+// !indirect_strict_weak_order<Comp, projected<iterator_t<R>, Proj>>
+static_assert(!HasIsHeapUntilRange<R<NoComparator*>>);
+
+template <class Iter, class Sent, size_t N>
+constexpr void test_one(std::array<int, N> input, size_t until_index) {
+  auto begin = Iter(input.data());
+  auto end = Sent(Iter(input.data() + input.size()));
+
+  { // (iterator, sentinel) overload.
+    std::same_as<Iter> decltype(auto) result = std::ranges::is_heap_until(begin, end);
+    assert(base(result) == input.data() + until_index);
+  }
+
+  { // (range) overload.
+    auto range = std::ranges::subrange(begin, end);
+    std::same_as<Iter> decltype(auto) result = std::ranges::is_heap_until(range);
+    assert(base(result) == input.data() + until_index);
+  }
+}
+
+template <class Iter, class Sent>
+constexpr void test_iter_sent() {
+  // Empty sequence.
+  test_one<Iter, Sent, 0>({}, 0);
+  // 1-element sequence.
+  test_one<Iter, Sent>(std::array{1}, 1);
+  // 2-element sequence, a heap.
+  test_one<Iter, Sent>(std::array{2, 1}, 2);
+  // 2-element sequence, not a heap.
+  test_one<Iter, Sent>(std::array{1, 2}, 1);
+  // Longer sequence, a heap.
+  test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 5, 2}, 8);
+  // Longer sequence, not a heap.
+  test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 2, 5}, 7);
+  // Longer sequence with duplicates, a heap.
+  test_one<Iter, Sent>(std::array{8, 7, 5, 5, 6, 4, 1, 2, 3, 2}, 10);
+  // Longer sequence with duplicates, not a heap.
+  test_one<Iter, Sent>(std::array{7, 5, 5, 6, 4, 1, 2, 3, 2, 8}, 3);
+  // All elements are the same.
+  test_one<Iter, Sent>(std::array{1, 1, 1}, 3);
+}
+
+template <class Iter>
+constexpr void test_iter() {
+  test_iter_sent<Iter, Iter>();
+  test_iter_sent<Iter, sentinel_wrapper<Iter>>();
+}
+
+constexpr void test_iterators() {
+  test_iter<random_access_iterator<int*>>();
+  test_iter<contiguous_iterator<int*>>();
+  test_iter<int*>();
+  test_iter<const int*>();
+}
 
 constexpr bool test() {
-  // TODO: main tests.
-  // TODO: A custom comparator works.
-  // TODO: A custom projection works.
+  test_iterators();
+
+  { // A custom comparator works.
+    std::ranges::less ls;
+    std::ranges::greater gt;
+    std::array in = {1, 3, 2, 5, 4, 7, 8, 6};
+
+    { // (iterator, sentinel) overload.
+      auto result_default_comp = std::ranges::is_heap_until(in.begin(), in.end(), ls);
+      assert(result_default_comp == in.begin() + 1);
+      auto result_custom_comp = std::ranges::is_heap_until(in.begin(), in.end(), gt);
+      assert(result_custom_comp == in.end());
+    }
+
+    { // (range) overload.
+      auto result_default_comp = std::ranges::is_heap_until(in, ls);
+      assert(result_default_comp == in.begin() + 1);
+      auto result_custom_comp = std::ranges::is_heap_until(in, gt);
+      assert(result_custom_comp == in.end());
+    }
+  }
+
+  { // A custom projection works.
+    struct A {
+      int x;
+      constexpr auto operator<=>(const A&) const = default;
+    };
+
+    std::array in = {A{-8}, A{-6}, A{-7}, A{-3}, A{-4}, A{-1}, A{-5}, A{-2}};
+    auto negate = [](A a) { return a.x * -1; };
+
+    { // (iterator, sentinel) overload.
+      auto result_default_comp = std::ranges::is_heap_until(in.begin(), in.end(), {});
+      assert(result_default_comp == in.begin() + 1);
+      auto result_custom_comp = std::ranges::is_heap_until(in.begin(), in.end(), {}, negate);
+      assert(result_custom_comp == in.end());
+    }
+
+    { // (range) overload.
+      auto result_default_comp = std::ranges::is_heap_until(in, {});
+      assert(result_default_comp == in.begin() + 1);
+      auto result_custom_comp = std::ranges::is_heap_until(in, {}, negate);
+      assert(result_custom_comp == in.end());
+    }
+  }
+
 
   return true;
 }

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 7e92e15d69010..40d1043d4b1b4 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -125,7 +125,7 @@ constexpr bool test_all() {
   dangling_1st(std::ranges::search_n, in, count, x);
   dangling_1st(std::ranges::find_end, in, in2);
   dangling_1st(std::ranges::is_sorted_until, in);
-  //dangling_1st(std::ranges::is_heap_until, in);
+  dangling_1st(std::ranges::is_heap_until, in);
   dangling_1st<for_each_result<dangling, decltype(unary_pred)>>(std::ranges::for_each, in, unary_pred);
   dangling_1st<copy_result<dangling, int*>>(std::ranges::copy, in, out);
   // TODO: uncomment `copy_backward` once https://reviews.llvm.org/D128864 lands.

diff  --git a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
index 7448f552364f2..f5f7160869368 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
@@ -107,8 +107,8 @@ constexpr bool test_all() {
   test(std::ranges::is_sorted, in, binary_pred);
   test(std::ranges::is_sorted_until, in, binary_pred);
   test(std::ranges::includes, in, in2, binary_pred);
-  //test(std::ranges::is_heap, in, binary_pred);
-  //test(std::ranges::is_heap_until, in, binary_pred);
+  test(std::ranges::is_heap, in, binary_pred);
+  test(std::ranges::is_heap_until, in, binary_pred);
   //std::ranges::clamp(2, 1, 3, binary_pred);
   //test(std::ranges::is_permutation, in, in2, binary_pred);
   test(std::ranges::copy_if, in, out, unary_pred);

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 38ca7c2522ff2..2da3db405c324 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
@@ -114,8 +114,8 @@ constexpr bool test_all() {
   test(std::ranges::is_sorted, in, &Foo::binary_pred, &Bar::val);
   test(std::ranges::is_sorted_until, in, &Foo::binary_pred, &Bar::val);
   test(std::ranges::includes, in, in2, &Foo::binary_pred, &Bar::val, &Bar::val);
-  //test(std::ranges::is_heap, in, &Foo::binary_pred, &Bar::val);
-  //test(std::ranges::is_heap_until, in, &Foo::binary_pred, &Bar::val);
+  test(std::ranges::is_heap, in, &Foo::binary_pred, &Bar::val);
+  test(std::ranges::is_heap_until, in, &Foo::binary_pred, &Bar::val);
   //std::ranges::clamp(b, a, c, &Foo::binary_pred);
   //test(std::ranges::is_permutation, in, in2, &Foo::binary_pred, &Bar::val, &Bar::val);
   test(std::ranges::for_each, in, &Foo::unary_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 c4aa662978b93..aba570160899a 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
@@ -101,8 +101,8 @@ constexpr void run_tests() {
   test(std::ranges::is_sorted, in);
   test(std::ranges::is_sorted_until, in);
   test(std::ranges::includes, in, in2);
-  //test(std::ranges::is_heap, in);
-  //test(std::ranges::is_heap_until, in);
+  test(std::ranges::is_heap, in);
+  test(std::ranges::is_heap_until, in);
   //test(std::ranges::is_permutation, in, in2);
   test(std::ranges::for_each, in, std::identity{});
   std::ranges::for_each_n(in.begin(), count, std::identity{});

diff  --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
index 9aa8024d86305..ebe78c1742216 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
@@ -88,8 +88,8 @@ static_assert(test(std::ranges::generate, a, gen));
 static_assert(test(std::ranges::generate_n, a, 10, gen));
 static_assert(test(std::ranges::includes, a, a));
 //static_assert(test(std::ranges::inplace_merge, a, a+5));
-//static_assert(test(std::ranges::is_heap, a));
-//static_assert(test(std::ranges::is_heap_until, a));
+static_assert(test(std::ranges::is_heap, a));
+static_assert(test(std::ranges::is_heap_until, a));
 static_assert(test(std::ranges::is_partitioned, a, odd));
 //static_assert(test(std::ranges::is_permutation, a, a));
 static_assert(test(std::ranges::is_sorted, a));


        


More information about the libcxx-commits mailing list