[libcxx-commits] [libcxx] [libc++] Make sure ranges algorithms and views handle boolean-testable correctly (PR #69378)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Sun Nov 5 06:12:04 PST 2023


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/69378

>From 6cdd8611e9497604e1beb9a19012773b83fa7cb7 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 17 Oct 2023 13:29:22 -0700
Subject: [PATCH 1/3] [libc++] Make sure ranges algorithms and views handle
 boolean-testable correctly

Before this patch, we would fail to implicitly convert the result of
predicates to bool, which means we'd potentially perform a copy or move
construction of the boolean-testable, which isn't allowed. The same
holds true for comparing iterators against sentinels, which is allowed
to return a boolean-testable type.

We already had tests aiming to ensure correct handling of these types,
but they failed to provide appropriate coverage in several cases due to
guaranteed RVO. This patch fixes the tests, adds tests for missing
algorithms and views, and fixes the actual problems in the code.

Fixes #69074
---
 libcxx/include/__algorithm/make_projected.h   |   2 +-
 .../include/__algorithm/ranges_find_if_not.h  |   4 +-
 libcxx/include/__algorithm/ranges_max.h       |   6 +-
 .../include/__algorithm/ranges_max_element.h  |   4 +-
 libcxx/include/__algorithm/ranges_remove.h    |   4 +-
 .../include/__algorithm/ranges_remove_copy.h  |   4 +-
 libcxx/include/__algorithm/ranges_replace.h   |   4 +-
 .../include/__algorithm/ranges_replace_copy.h |   4 +-
 .../include/__algorithm/ranges_upper_bound.h  |   4 +-
 .../ranges_uninitialized_algorithms.h         |   8 +-
 libcxx/include/__ranges/chunk_by_view.h       |  13 +-
 .../alg.remove/ranges.remove.pass.cpp         |   1 -
 .../alg.remove/ranges.remove_if.pass.cpp      |  15 -
 .../alg.replace/ranges.replace.pass.cpp       |   1 -
 .../alg.replace/ranges.replace_if.pass.cpp    |  14 -
 .../ranges.adjacent_find.pass.cpp             |  14 -
 .../ranges.find_first_of.pass.cpp             |  14 -
 .../alg.find/ranges.find.pass.cpp             |   1 -
 .../alg.find/ranges.find_if.pass.cpp          |  15 -
 .../alg.find/ranges.find_if_not.pass.cpp      |  15 -
 .../make.heap/ranges_make_heap.pass.cpp       |  17 --
 .../pop.heap/ranges_pop_heap.pass.cpp         |  17 --
 .../push.heap/ranges_push_heap.pass.cpp       |  17 --
 .../sort.heap/ranges_sort_heap.pass.cpp       |  17 --
 .../ranges.lexicographical_compare.pass.cpp   |  16 -
 .../ranges_nth_element.pass.cpp               |  20 --
 .../partial.sort/ranges_partial_sort.pass.cpp |   1 -
 .../alg.sort/sort/ranges.sort.pass.cpp        |  17 --
 .../stable.sort/ranges.stable.sort.pass.cpp   |  17 --
 ...es_robust_against_nonbool.compile.pass.cpp | 285 ++++++++++++++++++
 ...robust_against_nonbool_predicates.pass.cpp | 159 ----------
 .../robust_against_nonbool.compile.pass.cpp   |  57 ++++
 ...es_robust_against_nonbool.compile.pass.cpp |  69 +++++
 libcxx/test/support/boolean_testable.h        | 133 +++++++-
 libcxx/utils/data/ignore_format.txt           |   1 -
 35 files changed, 557 insertions(+), 433 deletions(-)
 create mode 100644 libcxx/test/std/algorithms/ranges_robust_against_nonbool.compile.pass.cpp
 delete mode 100644 libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/robust_against_nonbool.compile.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/ranges_robust_against_nonbool.compile.pass.cpp

diff --git a/libcxx/include/__algorithm/make_projected.h b/libcxx/include/__algorithm/make_projected.h
index ec854763a5a2916..3a86701118146e5 100644
--- a/libcxx/include/__algorithm/make_projected.h
+++ b/libcxx/include/__algorithm/make_projected.h
@@ -96,7 +96,7 @@ decltype(auto) __make_projected_comp(_Comp& __comp, _Proj1& __proj1, _Proj2& __p
     return __comp;
 
   } else {
-    return [&](auto&& __lhs, auto&& __rhs) {
+    return [&](auto&& __lhs, auto&& __rhs) -> bool {
       return std::invoke(__comp,
                         std::invoke(__proj1, std::forward<decltype(__lhs)>(__lhs)),
                         std::invoke(__proj2, std::forward<decltype(__rhs)>(__rhs)));
diff --git a/libcxx/include/__algorithm/ranges_find_if_not.h b/libcxx/include/__algorithm/ranges_find_if_not.h
index 6beade1462e099c..a18bea43165e0d8 100644
--- a/libcxx/include/__algorithm/ranges_find_if_not.h
+++ b/libcxx/include/__algorithm/ranges_find_if_not.h
@@ -39,14 +39,14 @@ struct __fn {
             indirect_unary_predicate<projected<_Ip, _Proj>> _Pred>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr _Ip
   operator()(_Ip __first, _Sp __last, _Pred __pred, _Proj __proj = {}) const {
-    auto __pred2 = [&](auto&& __e) { return !std::invoke(__pred, std::forward<decltype(__e)>(__e)); };
+    auto __pred2 = [&](auto&& __e) -> bool { return !std::invoke(__pred, std::forward<decltype(__e)>(__e)); };
     return ranges::__find_if_impl(std::move(__first), std::move(__last), __pred2, __proj);
   }
 
   template <input_range _Rp, class _Proj = identity, indirect_unary_predicate<projected<iterator_t<_Rp>, _Proj>> _Pred>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr borrowed_iterator_t<_Rp>
   operator()(_Rp&& __r, _Pred __pred, _Proj __proj = {}) const {
-    auto __pred2 = [&](auto&& __e) { return !std::invoke(__pred, std::forward<decltype(__e)>(__e)); };
+    auto __pred2 = [&](auto&& __e) -> bool { return !std::invoke(__pred, std::forward<decltype(__e)>(__e)); };
     return ranges::__find_if_impl(ranges::begin(__r), ranges::end(__r), __pred2, __proj);
   }
 };
diff --git a/libcxx/include/__algorithm/ranges_max.h b/libcxx/include/__algorithm/ranges_max.h
index 5cc418d3393cef6..782ce2670f05530 100644
--- a/libcxx/include/__algorithm/ranges_max.h
+++ b/libcxx/include/__algorithm/ranges_max.h
@@ -56,7 +56,7 @@ struct __fn {
   operator()(initializer_list<_Tp> __il, _Comp __comp = {}, _Proj __proj = {}) const {
     _LIBCPP_ASSERT_UNCATEGORIZED(__il.begin() != __il.end(), "initializer_list must contain at least one element");
 
-    auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) { return std::invoke(__comp, __rhs, __lhs); };
+    auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) -> bool { return std::invoke(__comp, __rhs, __lhs); };
     return *ranges::__min_element_impl(__il.begin(), __il.end(), __comp_lhs_rhs_swapped, __proj);
   }
 
@@ -72,7 +72,9 @@ struct __fn {
     _LIBCPP_ASSERT_UNCATEGORIZED(__first != __last, "range must contain at least one element");
 
     if constexpr (forward_range<_Rp> && !__is_cheap_to_copy<range_value_t<_Rp>>) {
-      auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) { return std::invoke(__comp, __rhs, __lhs); };
+      auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) -> bool {
+        return std::invoke(__comp, __rhs, __lhs);
+      };
       return *ranges::__min_element_impl(std::move(__first), std::move(__last), __comp_lhs_rhs_swapped, __proj);
     } else {
       range_value_t<_Rp> __result = *__first;
diff --git a/libcxx/include/__algorithm/ranges_max_element.h b/libcxx/include/__algorithm/ranges_max_element.h
index 2d92661c810999f..2ba97042f1f6e0f 100644
--- a/libcxx/include/__algorithm/ranges_max_element.h
+++ b/libcxx/include/__algorithm/ranges_max_element.h
@@ -37,7 +37,7 @@ struct __fn {
             indirect_strict_weak_order<projected<_Ip, _Proj>> _Comp = ranges::less>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr _Ip
   operator()(_Ip __first, _Sp __last, _Comp __comp = {}, _Proj __proj = {}) const {
-    auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) { return std::invoke(__comp, __rhs, __lhs); };
+    auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) -> bool { return std::invoke(__comp, __rhs, __lhs); };
     return ranges::__min_element_impl(__first, __last, __comp_lhs_rhs_swapped, __proj);
   }
 
@@ -46,7 +46,7 @@ struct __fn {
             indirect_strict_weak_order<projected<iterator_t<_Rp>, _Proj>> _Comp = ranges::less>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr borrowed_iterator_t<_Rp>
   operator()(_Rp&& __r, _Comp __comp = {}, _Proj __proj = {}) const {
-    auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) { return std::invoke(__comp, __rhs, __lhs); };
+    auto __comp_lhs_rhs_swapped = [&](auto&& __lhs, auto&& __rhs) -> bool { return std::invoke(__comp, __rhs, __lhs); };
     return ranges::__min_element_impl(ranges::begin(__r), ranges::end(__r), __comp_lhs_rhs_swapped, __proj);
   }
 };
diff --git a/libcxx/include/__algorithm/ranges_remove.h b/libcxx/include/__algorithm/ranges_remove.h
index bf0928df599ceb8..e27c4bdd733d816 100644
--- a/libcxx/include/__algorithm/ranges_remove.h
+++ b/libcxx/include/__algorithm/ranges_remove.h
@@ -36,7 +36,7 @@ struct __fn {
     requires indirect_binary_predicate<ranges::equal_to, projected<_Iter, _Proj>, const _Type*>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
   operator()(_Iter __first, _Sent __last, const _Type& __value, _Proj __proj = {}) const {
-    auto __pred = [&](auto&& __other) { return __value == __other; };
+    auto __pred = [&](auto&& __other) -> bool { return __value == __other; };
     return ranges::__remove_if_impl(std::move(__first), std::move(__last), __pred, __proj);
   }
 
@@ -45,7 +45,7 @@ struct __fn {
              indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const _Type*>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Range>
   operator()(_Range&& __range, const _Type& __value, _Proj __proj = {}) const {
-    auto __pred = [&](auto&& __other) { return __value == __other; };
+    auto __pred = [&](auto&& __other) -> bool { return __value == __other; };
     return ranges::__remove_if_impl(ranges::begin(__range), ranges::end(__range), __pred, __proj);
   }
 };
diff --git a/libcxx/include/__algorithm/ranges_remove_copy.h b/libcxx/include/__algorithm/ranges_remove_copy.h
index 457d593957adb5c..5158a78e4814057 100644
--- a/libcxx/include/__algorithm/ranges_remove_copy.h
+++ b/libcxx/include/__algorithm/ranges_remove_copy.h
@@ -47,7 +47,7 @@ struct __fn {
              indirect_binary_predicate<ranges::equal_to, projected<_InIter, _Proj>, const _Type*>
   _LIBCPP_HIDE_FROM_ABI constexpr remove_copy_result<_InIter, _OutIter>
   operator()(_InIter __first, _Sent __last, _OutIter __result, const _Type& __value, _Proj __proj = {}) const {
-    auto __pred = [&](auto&& __val) { return __value == __val; };
+    auto __pred = [&](auto&& __val) -> bool { return __value == __val; };
     return ranges::__remove_copy_if_impl(std::move(__first), std::move(__last), std::move(__result), __pred, __proj);
   }
 
@@ -56,7 +56,7 @@ struct __fn {
              indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const _Type*>
   _LIBCPP_HIDE_FROM_ABI constexpr remove_copy_result<borrowed_iterator_t<_Range>, _OutIter>
   operator()(_Range&& __range, _OutIter __result, const _Type& __value, _Proj __proj = {}) const {
-    auto __pred = [&](auto&& __val) { return __value == __val; };
+    auto __pred = [&](auto&& __val) -> bool { return __value == __val; };
     return ranges::__remove_copy_if_impl(
         ranges::begin(__range), ranges::end(__range), std::move(__result), __pred, __proj);
   }
diff --git a/libcxx/include/__algorithm/ranges_replace.h b/libcxx/include/__algorithm/ranges_replace.h
index 714fd5c7b089f28..b66a41aa8d0d779 100644
--- a/libcxx/include/__algorithm/ranges_replace.h
+++ b/libcxx/include/__algorithm/ranges_replace.h
@@ -36,7 +36,7 @@ struct __fn {
              indirect_binary_predicate<ranges::equal_to, projected<_Iter, _Proj>, const _Type1*>
   _LIBCPP_HIDE_FROM_ABI constexpr _Iter operator()(
       _Iter __first, _Sent __last, const _Type1& __old_value, const _Type2& __new_value, _Proj __proj = {}) const {
-    auto __pred = [&](const auto& __val) { return __val == __old_value; };
+    auto __pred = [&](const auto& __val) -> bool { return __val == __old_value; };
     return ranges::__replace_if_impl(std::move(__first), std::move(__last), __pred, __new_value, __proj);
   }
 
@@ -45,7 +45,7 @@ struct __fn {
              indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const _Type1*>
   _LIBCPP_HIDE_FROM_ABI constexpr borrowed_iterator_t<_Range>
   operator()(_Range&& __range, const _Type1& __old_value, const _Type2& __new_value, _Proj __proj = {}) const {
-    auto __pred = [&](auto&& __val) { return __val == __old_value; };
+    auto __pred = [&](auto&& __val) -> bool { return __val == __old_value; };
     return ranges::__replace_if_impl(ranges::begin(__range), ranges::end(__range), __pred, __new_value, __proj);
   }
 };
diff --git a/libcxx/include/__algorithm/ranges_replace_copy.h b/libcxx/include/__algorithm/ranges_replace_copy.h
index 124ff8f2c559d12..a7627024812fd28 100644
--- a/libcxx/include/__algorithm/ranges_replace_copy.h
+++ b/libcxx/include/__algorithm/ranges_replace_copy.h
@@ -53,7 +53,7 @@ struct __fn {
              const _OldType& __old_value,
              const _NewType& __new_value,
              _Proj __proj = {}) const {
-    auto __pred = [&](const auto& __value) { return __value == __old_value; };
+    auto __pred = [&](const auto& __value) -> bool { return __value == __old_value; };
     return ranges::__replace_copy_if_impl(
         std::move(__first), std::move(__last), std::move(__result), __pred, __new_value, __proj);
   }
@@ -68,7 +68,7 @@ struct __fn {
   _LIBCPP_HIDE_FROM_ABI constexpr replace_copy_result<borrowed_iterator_t<_Range>, _OutIter> operator()(
       _Range&& __range, _OutIter __result, const _OldType& __old_value, const _NewType& __new_value, _Proj __proj = {})
       const {
-    auto __pred = [&](const auto& __value) { return __value == __old_value; };
+    auto __pred = [&](const auto& __value) -> bool { return __value == __old_value; };
     return ranges::__replace_copy_if_impl(
         ranges::begin(__range), ranges::end(__range), std::move(__result), __pred, __new_value, __proj);
   }
diff --git a/libcxx/include/__algorithm/ranges_upper_bound.h b/libcxx/include/__algorithm/ranges_upper_bound.h
index a12a0e39b084909..7b571fb3448f94c 100644
--- a/libcxx/include/__algorithm/ranges_upper_bound.h
+++ b/libcxx/include/__algorithm/ranges_upper_bound.h
@@ -39,7 +39,7 @@ struct __fn {
             indirect_strict_weak_order<const _Type*, projected<_Iter, _Proj>> _Comp = ranges::less>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr _Iter
   operator()(_Iter __first, _Sent __last, const _Type& __value, _Comp __comp = {}, _Proj __proj = {}) const {
-    auto __comp_lhs_rhs_swapped = [&](const auto& __lhs, const auto& __rhs) {
+    auto __comp_lhs_rhs_swapped = [&](const auto& __lhs, const auto& __rhs) -> bool {
       return !std::invoke(__comp, __rhs, __lhs);
     };
 
@@ -52,7 +52,7 @@ struct __fn {
             indirect_strict_weak_order<const _Type*, projected<iterator_t<_Range>, _Proj>> _Comp = ranges::less>
   _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr borrowed_iterator_t<_Range>
   operator()(_Range&& __r, const _Type& __value, _Comp __comp = {}, _Proj __proj = {}) const {
-    auto __comp_lhs_rhs_swapped = [&](const auto& __lhs, const auto& __rhs) {
+    auto __comp_lhs_rhs_swapped = [&](const auto& __lhs, const auto& __rhs) -> bool {
       return !std::invoke(__comp, __rhs, __lhs);
     };
 
diff --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
index 108f6537538413b..96b6a9a99765688 100644
--- a/libcxx/include/__memory/ranges_uninitialized_algorithms.h
+++ b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
@@ -196,7 +196,7 @@ struct __fn {
   operator()(_InputIterator __ifirst, _Sentinel1 __ilast, _OutputIterator __ofirst, _Sentinel2 __olast) const {
     using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
 
-    auto __stop_copying = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+    auto __stop_copying = [&__olast](auto&& __out_iter) -> bool { return __out_iter == __olast; };
     auto __result       = std::__uninitialized_copy<_ValueType>(
         std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __stop_copying);
     return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
@@ -233,7 +233,7 @@ struct __fn {
   operator()(_InputIterator __ifirst, iter_difference_t<_InputIterator> __n,
              _OutputIterator __ofirst, _Sentinel __olast) const {
     using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
-    auto __stop_copying = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+    auto __stop_copying = [&__olast](auto&& __out_iter) -> bool { return __out_iter == __olast; };
     auto __result =
         std::__uninitialized_copy_n<_ValueType>(std::move(__ifirst), __n, std::move(__ofirst), __stop_copying);
     return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
@@ -263,7 +263,7 @@ struct __fn {
   operator()(_InputIterator __ifirst, _Sentinel1 __ilast, _OutputIterator __ofirst, _Sentinel2 __olast) const {
     using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
     auto __iter_move = [](auto&& __iter) -> decltype(auto) { return ranges::iter_move(__iter); };
-    auto __stop_moving = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+    auto __stop_moving = [&__olast](auto&& __out_iter) -> bool { return __out_iter == __olast; };
     auto __result      = std::__uninitialized_move<_ValueType>(
         std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __stop_moving, __iter_move);
     return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
@@ -301,7 +301,7 @@ struct __fn {
              _OutputIterator __ofirst, _Sentinel __olast) const {
     using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
     auto __iter_move = [](auto&& __iter) -> decltype(auto) { return ranges::iter_move(__iter); };
-    auto __stop_moving = [&__olast](auto&& __out_iter) { return __out_iter == __olast; };
+    auto __stop_moving = [&__olast](auto&& __out_iter) -> bool { return __out_iter == __olast; };
     auto __result      = std::__uninitialized_move_n<_ValueType>(
         std::move(__ifirst), __n, std::move(__ofirst), __stop_moving, __iter_move);
     return {_VSTD::move(__result.first), _VSTD::move(__result.second)};
diff --git a/libcxx/include/__ranges/chunk_by_view.h b/libcxx/include/__ranges/chunk_by_view.h
index cfb149b443571e8..e46998687646982 100644
--- a/libcxx/include/__ranges/chunk_by_view.h
+++ b/libcxx/include/__ranges/chunk_by_view.h
@@ -16,8 +16,6 @@
 #include <__config>
 #include <__functional/bind_back.h>
 #include <__functional/invoke.h>
-#include <__functional/not_fn.h>
-#include <__functional/reference_wrapper.h>
 #include <__iterator/concepts.h>
 #include <__iterator/default_sentinel.h>
 #include <__iterator/iterator_traits.h>
@@ -69,10 +67,11 @@ class chunk_by_view : public view_interface<chunk_by_view<_View, _Pred>> {
   _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> __find_next(iterator_t<_View> __current) {
     _LIBCPP_ASSERT_UNCATEGORIZED(
         __pred_.__has_value(), "Trying to call __find_next() on a chunk_by_view that does not have a valid predicate.");
-
-    return ranges::next(ranges::adjacent_find(__current, ranges::end(__base_), std::not_fn(std::ref(*__pred_))),
-                        1,
-                        ranges::end(__base_));
+    auto __reversed_pred = [this]<class _Tp, class _Up>(_Tp&& __x, _Up&& __y) -> bool {
+      return !std::invoke(*__pred_, std::forward<_Tp>(__x), std::forward<_Up>(__y));
+    };
+    return ranges::next(
+        ranges::adjacent_find(__current, ranges::end(__base_), __reversed_pred), 1, ranges::end(__base_));
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> __find_prev(iterator_t<_View> __current)
@@ -85,7 +84,7 @@ class chunk_by_view : public view_interface<chunk_by_view<_View, _Pred>> {
 
     auto __first = ranges::begin(__base_);
     reverse_view __reversed{subrange{__first, __current}};
-    auto __reversed_pred = [this]<class _Tp, class _Up>(_Tp&& __x, _Up&& __y) {
+    auto __reversed_pred = [this]<class _Tp, class _Up>(_Tp&& __x, _Up&& __y) -> bool {
       return !std::invoke(*__pred_, std::forward<_Up>(__y), std::forward<_Tp>(__x));
     };
     return ranges::prev(ranges::adjacent_find(__reversed, __reversed_pred).base(), 1, std::move(__first));
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove.pass.cpp
index 7836dede7342ae9..3f45ef9ab5166e0 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove.pass.cpp
@@ -25,7 +25,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 template <class Iter, class Sent = sentinel_wrapper<Iter>>
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove_if.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove_if.pass.cpp
index 1db4f171b91072b..9b8cf1121a13d16 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove_if.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove_if.pass.cpp
@@ -25,7 +25,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 struct FalsePredicate {
@@ -197,20 +196,6 @@ constexpr bool test() {
     }
   }
 
-  {
-    // check that the implicit conversion to bool works
-    {
-      int a[]  = {1, 2, 3, 4};
-      auto ret = std::ranges::remove_if(a, a + 4, [](const int& i) { return BooleanTestable{i == 3}; });
-      assert(ret.begin() == a + 3);
-    }
-    {
-      int a[]  = {1, 2, 3, 4};
-      auto ret = std::ranges::remove_if(a, [](const int& b) { return BooleanTestable{b == 3}; });
-      assert(ret.begin() == a + 3);
-    }
-  }
-
   return true;
 }
 // clang-format on
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp
index 92ba59c1ddb2b4a..8721882bbe4e9b8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp
@@ -27,7 +27,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 template <class Iter, class Sent = sentinel_wrapper<Iter>>
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace_if.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace_if.pass.cpp
index 67e5e87af35be00..79098b090873c0d 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace_if.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace_if.pass.cpp
@@ -26,7 +26,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 struct FalsePredicate {
@@ -133,19 +132,6 @@ constexpr bool test() {
     }
   }
 
-  { // check that the implicit conversion to bool works
-    {
-      int a[] = {1, 2, 2, 4};
-      auto ret = std::ranges::replace_if(std::begin(a), std::end(a), [](int) { return BooleanTestable{false}; }, 2);
-      assert(ret == std::end(a));
-    }
-    {
-      int a[] = {1, 2, 2, 4};
-      auto ret = std::ranges::replace_if(a, [](int) { return BooleanTestable{false}; }, 2);
-      assert(ret == std::end(a));
-    }
-  }
-
   { // check that std::ranges::dangling is returned
     [[maybe_unused]] std::same_as<std::ranges::dangling> decltype(auto) ret =
         std::ranges::replace_if(std::array {1, 2, 3, 4}, [](int) { return false; }, 1);
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/ranges.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/ranges.adjacent_find.pass.cpp
index d2e0bb5d182d69c..7558f6b968132ee 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/ranges.adjacent_find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/ranges.adjacent_find.pass.cpp
@@ -26,7 +26,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 template <class Iter, class Sent = Iter>
@@ -172,19 +171,6 @@ constexpr bool test() {
     }
   }
 
-  { // check that the implicit conversion to bool works
-    {
-      int a[] = {1, 2, 2, 4};
-      auto ret = std::ranges::adjacent_find(a, a + 4, [](int i, int j) { return BooleanTestable{i == j}; });
-      assert(ret == a + 1);
-    }
-    {
-      int a[] = {1, 2, 2, 4};
-      auto ret = std::ranges::adjacent_find(a, [](int i, int j) { return BooleanTestable{i == j}; });
-      assert(ret == a + 1);
-    }
-  }
-
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/ranges.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/ranges.find_first_of.pass.cpp
index cea30420428cd55..a74e6d721f773bf 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/ranges.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/ranges.find_first_of.pass.cpp
@@ -30,7 +30,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 template <class Iter1, class Iter2 = int*, class Sent1 = Iter1, class Sent2 = Iter2>
@@ -194,19 +193,6 @@ constexpr bool test() {
     }
   }
 
-  { // check that the implicit conversion to bool works
-    StrictComparable<int> a[] = {1, 2, 3, 4};
-    StrictComparable<int> b[] = {2, 3};
-    {
-      auto ret = std::ranges::find_first_of(a, std::end(a), b, std::end(b));
-      assert(ret == a + 1);
-    }
-    {
-      auto ret = std::ranges::find_first_of(a, b);
-      assert(ret == a + 1);
-    }
-  }
-
   { // check that the complexity requirements are met
     int a[] = {1, 2, 3, 4};
     int b[] = {2, 3};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
index a1fb92a984e915c..22f938f73ae078a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
@@ -27,7 +27,6 @@
 #include <vector>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 struct NotEqualityComparable {};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_if.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_if.pass.cpp
index 3f7ec622f145ef0..2585feba5614e0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_if.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_if.pass.cpp
@@ -24,7 +24,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 struct Predicate {
@@ -224,20 +223,6 @@ constexpr bool test() {
     }
   }
 
-  {
-    // check that the implicit conversion to bool works
-    {
-      int a[] = {1, 2, 3, 4};
-      auto ret = std::ranges::find_if(a, a + 4, [](const int& i) { return BooleanTestable{i == 3}; });
-      assert(ret == a + 2);
-    }
-    {
-      int a[] = {1, 2, 3, 4};
-      auto ret = std::ranges::find_if(a, [](const int& b) { return BooleanTestable{b == 3}; });
-      assert(ret == a + 2);
-    }
-  }
-
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_if_not.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_if_not.pass.cpp
index 95860745f56204e..171ffefa75cf977 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_if_not.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_if_not.pass.cpp
@@ -24,7 +24,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 struct Predicate {
@@ -218,20 +217,6 @@ constexpr bool test() {
     }
   }
 
-  {
-    // check that the implicit conversion to bool works
-    {
-      int a[] = {1, 2, 3, 4};
-      auto ret = std::ranges::find_if_not(a, a + 4, [](const int& i) { return BooleanTestable{i != 3}; });
-      assert(ret == a + 2);
-    }
-    {
-      int a[] = {1, 2, 3, 4};
-      auto ret = std::ranges::find_if_not(a, [](const int& b) { return BooleanTestable{b != 3}; });
-      assert(ret == a + 2);
-    }
-  }
-
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/make.heap/ranges_make_heap.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/make.heap/ranges_make_heap.pass.cpp
index 5d8086df450abb8..6fc6f3ee1bf25e6 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/make.heap/ranges_make_heap.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/make.heap/ranges_make_heap.pass.cpp
@@ -28,7 +28,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 // SFINAE tests.
@@ -191,22 +190,6 @@ constexpr bool test() {
     }
   }
 
-  { // The comparator can return any type that's convertible to `bool`.
-    const std::array input = {2, 1, 3};
-    std::array expected = {3, 1, 2};
-    {
-      auto in = input;
-      auto last = std::ranges::make_heap(in.begin(), in.end(), [](int i, int j) { return BooleanTestable{i < j}; });
-      verify_heap(in, last, expected);
-    }
-
-    {
-      auto in = input;
-      auto last = std::ranges::make_heap(in, [](int i, int j) { return BooleanTestable{i < j}; });
-      verify_heap(in, last, expected);
-    }
-  }
-
   { // `std::ranges::dangling` is returned.
     [[maybe_unused]] std::same_as<std::ranges::dangling> decltype(auto) result =
         std::ranges::make_heap(std::array{1, 2, 3});
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/ranges_pop_heap.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/ranges_pop_heap.pass.cpp
index e190586fef813bb..9ffde7acc3b65a9 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/ranges_pop_heap.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/pop.heap/ranges_pop_heap.pass.cpp
@@ -28,7 +28,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 // SFINAE tests.
@@ -190,22 +189,6 @@ constexpr bool test() {
     }
   }
 
-  { // The comparator can return any type that's convertible to `bool`.
-    const std::array input = {3, 1, 2};
-    std::array expected = {2, 1, 3};
-    {
-      auto in = input;
-      auto last = std::ranges::pop_heap(in.begin(), in.end(), [](int i, int j) { return BooleanTestable{i < j}; });
-      verify_heap(in, last, expected);
-    }
-
-    {
-      auto in = input;
-      auto last = std::ranges::pop_heap(in, [](int i, int j) { return BooleanTestable{i < j}; });
-      verify_heap(in, last, expected);
-    }
-  }
-
   { // `std::ranges::dangling` is returned.
     [[maybe_unused]] std::same_as<std::ranges::dangling> decltype(auto) result =
         std::ranges::pop_heap(std::array{2, 1, 3});
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/push.heap/ranges_push_heap.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/push.heap/ranges_push_heap.pass.cpp
index 331f07755fae49e..082de929efae830 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/push.heap/ranges_push_heap.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/push.heap/ranges_push_heap.pass.cpp
@@ -28,7 +28,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 // SFINAE tests.
@@ -193,22 +192,6 @@ constexpr bool test() {
     }
   }
 
-  { // The comparator can return any type that's convertible to `bool`.
-    const std::array input = {2, 1, 3};
-    std::array expected = {3, 1, 2};
-    {
-      auto in = input;
-      auto last = std::ranges::push_heap(in.begin(), in.end(), [](int i, int j) { return BooleanTestable{i < j}; });
-      verify_heap(in, last, expected);
-    }
-
-    {
-      auto in = input;
-      auto last = std::ranges::push_heap(in, [](int i, int j) { return BooleanTestable{i < j}; });
-      verify_heap(in, last, expected);
-    }
-  }
-
   { // `std::ranges::dangling` is returned.
     [[maybe_unused]] std::same_as<std::ranges::dangling> decltype(auto) result =
         std::ranges::push_heap(std::array{1, 2, 3});
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp
index 5723ed0d3db25e4..245d163f984c194 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/sort.heap/ranges_sort_heap.pass.cpp
@@ -29,7 +29,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 // SFINAE tests.
@@ -189,22 +188,6 @@ constexpr bool test() {
     }
   }
 
-  { // The comparator can return any type that's convertible to `bool`.
-    const std::array input = {3, 1, 2};
-    std::array expected = {1, 2, 3};
-    {
-      auto in = input;
-      auto last = std::ranges::sort_heap(in.begin(), in.end(), [](int i, int j) { return BooleanTestable{i < j}; });
-      verify_sorted(in, last, expected);
-    }
-
-    {
-      auto in = input;
-      auto last = std::ranges::sort_heap(in, [](int i, int j) { return BooleanTestable{i < j}; });
-      verify_sorted(in, last, expected);
-    }
-  }
-
   { // `std::ranges::dangling` is returned.
     [[maybe_unused]] std::same_as<std::ranges::dangling> decltype(auto) result =
         std::ranges::sort_heap(std::array{3, 1, 2});
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.lex.comparison/ranges.lexicographical_compare.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.lex.comparison/ranges.lexicographical_compare.pass.cpp
index 058be468d35b669..3c03af7db05cd0c 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.lex.comparison/ranges.lexicographical_compare.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.lex.comparison/ranges.lexicographical_compare.pass.cpp
@@ -31,7 +31,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 template <class Iter1, class Sent1 = Iter1, class Iter2 = int*, class Sent2 = int*>
@@ -185,21 +184,6 @@ constexpr bool test() {
     }
   }
 
-  { // check that the implicit conversion to bool works
-    {
-      int a[] = {1, 2, 3, 4};
-      auto ret = std::ranges::lexicographical_compare(std::begin(a), std::end(a),
-                                                      std::begin(a), std::end(a),
-                                                      [](int i, int j) { return BooleanTestable{i < j}; });
-      assert(!ret);
-    }
-    {
-      int a[] = {1, 2, 3, 4};
-      auto ret = std::ranges::lexicographical_compare(a, a, [](int i, int j) { return BooleanTestable{i < j}; });
-      assert(!ret);
-    }
-  }
-
   { // check that the complexity requirements are met
     {
       int predCount = 0;
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/ranges_nth_element.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/ranges_nth_element.pass.cpp
index ad3c8ab699ab442..48816305c894435 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/ranges_nth_element.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.nth.element/ranges_nth_element.pass.cpp
@@ -30,7 +30,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 // SFINAE tests.
@@ -257,25 +256,6 @@ constexpr bool test() {
     }
   }
 
-  { // The comparator can return any type that's convertible to `bool`.
-    const std::array input = {2, 1, 3};
-    auto pred = [](int i, int j) { return BooleanTestable{i < j}; };
-
-    {
-      std::array in = input;
-      auto last = std::ranges::nth_element(in.begin(), in.begin() + 1, in.end(), pred);
-      assert(in[1] == 2);
-      assert(last == in.end());
-    }
-
-    {
-      std::array in = input;
-      auto last = std::ranges::nth_element(in, in.begin() + 1, pred);
-      assert(in[1] == 2);
-      assert(last == in.end());
-    }
-  }
-
   { // `std::ranges::dangling` is returned.
     std::array in{1, 2, 3};
     [[maybe_unused]] std::same_as<std::ranges::dangling> decltype(auto) result =
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/partial.sort/ranges_partial_sort.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/partial.sort/ranges_partial_sort.pass.cpp
index af304662599ca70..f348c6e93e19c99 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/partial.sort/ranges_partial_sort.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/partial.sort/ranges_partial_sort.pass.cpp
@@ -28,7 +28,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 // SFINAE tests.
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/sort/ranges.sort.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/sort/ranges.sort.pass.cpp
index 2463bf733196d01..d7bf6dd75f90fb3 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/sort/ranges.sort.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/sort/ranges.sort.pass.cpp
@@ -28,7 +28,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 // SFINAE tests.
@@ -184,22 +183,6 @@ constexpr bool test() {
     }
   }
 
-  { // The comparator can return any type that's convertible to `bool`.
-    {
-      std::array in = {2, 1, 3};
-      auto last = std::ranges::sort(in.begin(), in.end(), [](int i, int j) { return BooleanTestable{i < j}; });
-      assert((in == std::array{1, 2, 3}));
-      assert(last == in.end());
-    }
-
-    {
-      std::array in = {2, 1, 3};
-      auto last = std::ranges::sort(in, [](int i, int j) { return BooleanTestable{i < j}; });
-      assert((in == std::array{1, 2, 3}));
-      assert(last == in.end());
-    }
-  }
-
   { // `std::ranges::dangling` is returned.
     [[maybe_unused]] std::same_as<std::ranges::dangling> decltype(auto) result = std::ranges::sort(std::array{1, 2, 3});
   }
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/ranges.stable.sort.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/ranges.stable.sort.pass.cpp
index ea0a3ebdf85b26d..c8f76b20c682544 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/ranges.stable.sort.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/ranges.stable.sort.pass.cpp
@@ -27,7 +27,6 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
-#include "boolean_testable.h"
 #include "test_iterators.h"
 
 // SFINAE tests.
@@ -238,22 +237,6 @@ void test() {
     }
   }
 
-  { // The comparator can return any type that's convertible to `bool`.
-    {
-      std::array in = {2, 1, 3};
-      auto last = std::ranges::stable_sort(in.begin(), in.end(), [](int i, int j) { return BooleanTestable{i < j}; });
-      assert((in == std::array{1, 2, 3}));
-      assert(last == in.end());
-    }
-
-    {
-      std::array in = {2, 1, 3};
-      auto last = std::ranges::stable_sort(in, [](int i, int j) { return BooleanTestable{i < j}; });
-      assert((in == std::array{1, 2, 3}));
-      assert(last == in.end());
-    }
-  }
-
   { // `std::ranges::dangling` is returned.
     [[maybe_unused]] std::same_as<std::ranges::dangling> decltype(auto) result =
         std::ranges::stable_sort(std::array{1, 2, 3});
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_nonbool.compile.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_nonbool.compile.pass.cpp
new file mode 100644
index 000000000000000..a5668f20ccfd250
--- /dev/null
+++ b/libcxx/test/std/algorithms/ranges_robust_against_nonbool.compile.pass.cpp
@@ -0,0 +1,285 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <algorithm>
+//
+// Range algorithms that take predicates should support predicates that return a non-boolean value as long as the
+// returned type is implicitly convertible to bool.
+
+#include <algorithm>
+
+#include <initializer_list>
+#include <ranges>
+
+#include "boolean_testable.h"
+#include "test_macros.h"
+
+using Value     = StrictComparable<int>;
+using Iterator  = StrictBooleanIterator<Value*>;
+using Range     = std::ranges::subrange<Iterator>;
+auto pred1      = StrictUnaryPredicate;
+auto pred2      = StrictBinaryPredicate;
+auto projection = [](Value const& val) -> Value { return val; };
+
+void f(Iterator it, Range in, Iterator out, std::size_t n, Value const& val, std::initializer_list<Value> ilist) {
+  // Functions of the form (in, pred)
+  auto in_pred = [&](auto func, auto pred) {
+    (void)func(it, it, pred);
+    (void)func(in, pred);
+    (void)func(it, it, pred, projection);
+    (void)func(in, pred, projection);
+  };
+
+  // Functions of the form (in, in, pred)
+  auto in_in_pred = [&](auto func, auto pred) {
+    (void)func(it, it, it, it, pred);
+    (void)func(in, in, pred);
+    (void)func(it, it, it, it, pred, projection);
+    (void)func(in, in, pred, projection);
+  };
+
+  in_pred(std::ranges::any_of, pred1);
+  in_pred(std::ranges::all_of, pred1);
+#if TEST_STD_VER >= 23
+  in_in_pred(std::ranges::ends_with, pred2);
+#endif
+  in_pred(std::ranges::none_of, pred1);
+  in_pred(std::ranges::find_if, pred1);
+  in_pred(std::ranges::find_if_not, pred1);
+  in_in_pred(std::ranges::find_first_of, pred2);
+  in_pred(std::ranges::adjacent_find, pred2);
+  in_in_pred(std::ranges::mismatch, pred2);
+  in_in_pred(std::ranges::equal, pred2);
+  in_in_pred(std::ranges::lexicographical_compare, pred2);
+  in_pred(std::ranges::partition_point, pred1);
+  // lower_bound
+  {
+    (void)std::ranges::lower_bound(it, it, val, pred2);
+    (void)std::ranges::lower_bound(in, val, pred2);
+    (void)std::ranges::lower_bound(it, it, val, pred2, projection);
+    (void)std::ranges::lower_bound(in, val, pred2, projection);
+  }
+  // upper_bound
+  {
+    (void)std::ranges::upper_bound(it, it, val, pred2);
+    (void)std::ranges::upper_bound(in, val, pred2);
+    (void)std::ranges::upper_bound(it, it, val, pred2, projection);
+    (void)std::ranges::upper_bound(in, val, pred2, projection);
+  }
+  // equal_range
+  {
+    (void)std::ranges::equal_range(it, it, val, pred2);
+    (void)std::ranges::equal_range(in, val, pred2);
+    (void)std::ranges::equal_range(it, it, val, pred2, projection);
+    (void)std::ranges::equal_range(in, val, pred2, projection);
+  }
+  // binary_search
+  {
+    (void)std::ranges::binary_search(it, it, val, pred2);
+    (void)std::ranges::binary_search(in, val, pred2);
+    (void)std::ranges::binary_search(it, it, val, pred2, projection);
+    (void)std::ranges::binary_search(in, val, pred2, projection);
+  }
+  // min
+  {
+    (void)std::ranges::min(val, val, pred2);
+    (void)std::ranges::min(val, val, pred2, projection);
+    (void)std::ranges::min(ilist, pred2);
+    (void)std::ranges::min(ilist, pred2, projection);
+    (void)std::ranges::min(in, pred2);
+    (void)std::ranges::min(in, pred2, projection);
+  }
+  // max
+  {
+    (void)std::ranges::max(val, val, pred2);
+    (void)std::ranges::max(val, val, pred2, projection);
+    (void)std::ranges::max(ilist, pred2);
+    (void)std::ranges::max(ilist, pred2, projection);
+    (void)std::ranges::max(in, pred2);
+    (void)std::ranges::max(in, pred2, projection);
+  }
+  // minmax
+  {
+    (void)std::ranges::minmax(val, val, pred2);
+    (void)std::ranges::minmax(val, val, pred2, projection);
+    (void)std::ranges::minmax(ilist, pred2);
+    (void)std::ranges::minmax(ilist, pred2, projection);
+    (void)std::ranges::minmax(in, pred2);
+    (void)std::ranges::minmax(in, pred2, projection);
+  }
+
+  in_pred(std::ranges::min_element, pred2);
+  in_pred(std::ranges::max_element, pred2);
+  in_pred(std::ranges::minmax_element, pred2);
+  in_pred(std::ranges::count_if, pred1);
+  in_in_pred(std::ranges::search, pred2);
+  // search_n
+  {
+    (void)std::ranges::search_n(it, it, n, val, pred2);
+    (void)std::ranges::search_n(in, n, val, pred2);
+    (void)std::ranges::search_n(it, it, n, val, pred2, projection);
+    (void)std::ranges::search_n(in, n, val, pred2, projection);
+  }
+  in_in_pred(std::ranges::find_end, pred2);
+  in_pred(std::ranges::is_partitioned, pred1);
+  in_pred(std::ranges::is_sorted, pred2);
+  in_pred(std::ranges::is_sorted_until, pred2);
+  in_in_pred(std::ranges::includes, pred2);
+  in_pred(std::ranges::is_heap, pred2);
+  in_pred(std::ranges::is_heap_until, pred2);
+  // clamp
+  {
+    (void)std::ranges::clamp(val, val, val);
+    (void)std::ranges::clamp(val, val, val, pred2);
+    (void)std::ranges::clamp(val, val, val, pred2, projection);
+  }
+  in_in_pred(std::ranges::is_permutation, pred2);
+  // copy_if
+  {
+    (void)std::ranges::copy_if(it, it, out, pred1);
+    (void)std::ranges::copy_if(in, out, pred1);
+    (void)std::ranges::copy_if(it, it, out, pred1, projection);
+    (void)std::ranges::copy_if(in, out, pred1, projection);
+  }
+  {
+    (void)std::ranges::remove_copy_if(it, it, out, pred1);
+    (void)std::ranges::remove_copy_if(in, out, pred1);
+    (void)std::ranges::remove_copy_if(it, it, out, pred1, projection);
+    (void)std::ranges::remove_copy_if(in, out, pred1, projection);
+  }
+  // remove_copy
+  {
+    (void)std::ranges::remove_copy(it, it, out, val);
+    (void)std::ranges::remove_copy(in, out, val);
+    (void)std::ranges::remove_copy(it, it, out, val, projection);
+    (void)std::ranges::remove_copy(in, out, val, projection);
+  }
+  // replace
+  {
+    (void)std::ranges::replace(it, it, val, val);
+    (void)std::ranges::replace(in, val, val);
+    (void)std::ranges::replace(it, it, val, val, projection);
+    (void)std::ranges::replace(in, val, val, projection);
+  }
+  // replace_if
+  {
+    (void)std::ranges::replace_if(it, it, pred1, val);
+    (void)std::ranges::replace_if(in, pred1, val);
+    (void)std::ranges::replace_if(it, it, pred1, val, projection);
+    (void)std::ranges::replace_if(in, pred1, val, projection);
+  }
+  // replace_copy_if
+  {
+    (void)std::ranges::replace_copy_if(it, it, out, pred1, val);
+    (void)std::ranges::replace_copy_if(in, out, pred1, val);
+    (void)std::ranges::replace_copy_if(it, it, out, pred1, val, projection);
+    (void)std::ranges::replace_copy_if(in, out, pred1, val, projection);
+  }
+  // replace_copy
+  {
+    (void)std::ranges::replace_copy(it, it, out, val, val);
+    (void)std::ranges::replace_copy(in, out, val, val);
+    (void)std::ranges::replace_copy(it, it, out, val, val, projection);
+    (void)std::ranges::replace_copy(in, out, val, val, projection);
+  }
+  // unique_copy
+  {
+    (void)std::ranges::unique_copy(it, it, out, pred2);
+    (void)std::ranges::unique_copy(in, out, pred2);
+    (void)std::ranges::unique_copy(it, it, out, pred2, projection);
+    (void)std::ranges::unique_copy(in, out, pred2, projection);
+  }
+  // partition_copy
+  {
+    (void)std::ranges::partition_copy(it, it, out, out, pred1);
+    (void)std::ranges::partition_copy(in, out, out, pred1);
+    (void)std::ranges::partition_copy(it, it, out, out, pred1, projection);
+    (void)std::ranges::partition_copy(in, out, out, pred1, projection);
+  }
+  in_in_pred(std::ranges::partial_sort_copy, pred2);
+#if TEST_STD_VER > 20
+  in_in_pred(std::ranges::starts_with, pred2);
+#endif
+  // merge
+  {
+    (void)std::ranges::merge(it, it, it, it, out, pred2);
+    (void)std::ranges::merge(in, in, out, pred2);
+    (void)std::ranges::merge(it, it, it, it, out, pred2, projection, projection);
+    (void)std::ranges::merge(in, in, out, pred2, projection, projection);
+  }
+  // set_difference
+  {
+    (void)std::ranges::set_difference(it, it, it, it, out, pred2);
+    (void)std::ranges::set_difference(in, in, out, pred2);
+    (void)std::ranges::set_difference(it, it, it, it, out, pred2, projection, projection);
+    (void)std::ranges::set_difference(in, in, out, pred2, projection, projection);
+  }
+  // set_intersection
+  {
+    (void)std::ranges::set_intersection(it, it, it, it, out, pred2);
+    (void)std::ranges::set_intersection(in, in, out, pred2);
+    (void)std::ranges::set_intersection(it, it, it, it, out, pred2, projection, projection);
+    (void)std::ranges::set_intersection(in, in, out, pred2, projection, projection);
+  }
+  // set_symmetric_difference
+  {
+    (void)std::ranges::set_symmetric_difference(it, it, it, it, out, pred2);
+    (void)std::ranges::set_symmetric_difference(in, in, out, pred2);
+    (void)std::ranges::set_symmetric_difference(it, it, it, it, out, pred2, projection, projection);
+    (void)std::ranges::set_symmetric_difference(in, in, out, pred2, projection, projection);
+  }
+  // set_union
+  {
+    (void)std::ranges::set_union(it, it, it, it, out, pred2);
+    (void)std::ranges::set_union(in, in, out, pred2);
+    (void)std::ranges::set_union(it, it, it, it, out, pred2, projection, projection);
+    (void)std::ranges::set_union(in, in, out, pred2, projection, projection);
+  }
+  in_pred(std::ranges::remove_if, pred1);
+  // remove
+  {
+    (void)std::ranges::remove(it, it, val);
+    (void)std::ranges::remove(it, it, val, projection);
+    (void)std::ranges::remove(in, val);
+    (void)std::ranges::remove(in, val, projection);
+  }
+  in_pred(std::ranges::unique, pred2);
+  in_pred(std::ranges::partition, pred1);
+  in_pred(std::ranges::stable_partition, pred1);
+  in_pred(std::ranges::sort, pred2);
+  in_pred(std::ranges::stable_sort, pred2);
+  // partial_sort
+  {
+    (void)std::ranges::partial_sort(it, it, it, pred2);
+    (void)std::ranges::partial_sort(in, it, pred2);
+    (void)std::ranges::partial_sort(it, it, it, pred2, projection);
+    (void)std::ranges::partial_sort(in, it, pred2, projection);
+  }
+  // nth_element
+  {
+    (void)std::ranges::nth_element(it, it, it, pred2);
+    (void)std::ranges::nth_element(in, it, pred2);
+    (void)std::ranges::nth_element(it, it, it, pred2, projection);
+    (void)std::ranges::nth_element(in, it, pred2, projection);
+  }
+  // inplace_merge
+  {
+    (void)std::ranges::inplace_merge(it, it, it, pred2);
+    (void)std::ranges::inplace_merge(in, it, pred2);
+    (void)std::ranges::inplace_merge(it, it, it, pred2, projection);
+    (void)std::ranges::inplace_merge(in, it, pred2, projection);
+  }
+  in_pred(std::ranges::make_heap, pred2);
+  in_pred(std::ranges::push_heap, pred2);
+  in_pred(std::ranges::pop_heap, pred2);
+  in_pred(std::ranges::sort_heap, pred2);
+  in_pred(std::ranges::prev_permutation, pred2);
+  in_pred(std::ranges::next_permutation, pred2);
+}
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
deleted file mode 100644
index 04773f0f5bc802e..000000000000000
--- a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// <algorithm>
-//
-// Range algorithms that take predicates should support predicates that return a non-boolean value as long as the
-// returned type is implicitly convertible to bool.
-
-#include <algorithm>
-
-#include <array>
-#include <concepts>
-#include <initializer_list>
-#include <iterator>
-#include <ranges>
-
-#include "boolean_testable.h"
-#include "test_macros.h"
-
-constexpr auto unary_pred = [](int i) { return BooleanTestable(i > 0); };
-static_assert(!std::same_as<decltype(unary_pred(1)), bool>);
-static_assert(std::convertible_to<decltype(unary_pred(1)), bool>);
-
-constexpr auto binary_pred = [](int i, int j) { return BooleanTestable(i < j); };
-static_assert(!std::same_as<decltype(binary_pred(1, 2)), bool>);
-static_assert(std::convertible_to<decltype(binary_pred(1, 2)), bool>);
-
-// Invokes both the (iterator, sentinel, ...) and the (range, ...) overloads of the given niebloid.
-
-// (in, ...)
-template <class Func, std::ranges::range Input, class... Args>
-constexpr void test(Func&& func, Input& in, Args&&... args) {
-  (void)func(in.begin(), in.end(), std::forward<Args>(args)...);
-  (void)func(in, std::forward<Args>(args)...);
-}
-
-// (in1, in2, ...)
-template <class Func, std::ranges::range Input, class... Args>
-constexpr void test(Func&& func, Input& in1, Input& in2, Args&&... args) {
-  (void)func(in1.begin(), in1.end(), in2.begin(), in2.end(), std::forward<Args>(args)...);
-  (void)func(in1, in2, std::forward<Args>(args)...);
-}
-
-// (in, mid, ...)
-template <class Func, std::ranges::range Input, class... Args>
-constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&&... args) {
-  (void)func(in.begin(), mid, in.end(), std::forward<Args>(args)...);
-  (void)func(in, mid, std::forward<Args>(args)...);
-}
-
-constexpr bool test_all() {
-  std::array in  = {1, 2, 3};
-  std::array in2 = {4, 5, 6};
-  auto mid       = in.begin() + 1;
-
-  std::array output = {7, 8, 9, 10, 11, 12};
-  auto out          = output.begin();
-  auto out2         = output.begin() + 1;
-
-  int x     = 2;
-  int count = 1;
-
-  test(std::ranges::any_of, in, unary_pred);
-  test(std::ranges::all_of, in, unary_pred);
-#if TEST_STD_VER >= 23
-  test(std::ranges::ends_with, in, in2, binary_pred);
-#endif
-  test(std::ranges::none_of, in, unary_pred);
-  test(std::ranges::find_if, in, unary_pred);
-  test(std::ranges::find_if_not, in, unary_pred);
-  test(std::ranges::find_first_of, in, in2, binary_pred);
-  test(std::ranges::adjacent_find, in, binary_pred);
-  test(std::ranges::mismatch, in, in2, binary_pred);
-  test(std::ranges::equal, in, in2, binary_pred);
-  test(std::ranges::lexicographical_compare, in, in2, binary_pred);
-  test(std::ranges::partition_point, in, unary_pred);
-  test(std::ranges::lower_bound, in, x, binary_pred);
-  test(std::ranges::upper_bound, in, x, binary_pred);
-  test(std::ranges::equal_range, in, x, binary_pred);
-  test(std::ranges::binary_search, in, x, binary_pred);
-
-  // min
-  (void)std::ranges::min(1, 2, binary_pred);
-  (void)std::ranges::min(std::initializer_list<int>{1, 2}, binary_pred);
-  (void)std::ranges::min(in, binary_pred);
-  // max
-  (void)std::ranges::max(1, 2, binary_pred);
-  (void)std::ranges::max(std::initializer_list<int>{1, 2}, binary_pred);
-  (void)std::ranges::max(in, binary_pred);
-  // minmax
-  (void)std::ranges::minmax(1, 2, binary_pred);
-  (void)std::ranges::minmax(std::initializer_list<int>{1, 2}, binary_pred);
-  (void)std::ranges::minmax(in, binary_pred);
-
-  test(std::ranges::min_element, in, binary_pred);
-  test(std::ranges::max_element, in, binary_pred);
-  test(std::ranges::minmax_element, in, binary_pred);
-  test(std::ranges::count_if, in, unary_pred);
-  test(std::ranges::search, in, in2, binary_pred);
-  test(std::ranges::search_n, in, count, x, binary_pred);
-  test(std::ranges::find_end, in, in2, binary_pred);
-  test(std::ranges::is_partitioned, in, unary_pred);
-  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);
-  (void)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);
-  test(std::ranges::remove_copy_if, in, out, unary_pred);
-  test(std::ranges::replace_if, in, unary_pred, x);
-  test(std::ranges::replace_copy_if, in, out, unary_pred, x);
-  test(std::ranges::unique_copy, in, out, binary_pred);
-  test(std::ranges::partition_copy, in, out, out2, unary_pred);
-  test(std::ranges::partial_sort_copy, in, in2, binary_pred);
-  test(std::ranges::merge, in, in2, out, binary_pred);
-#if TEST_STD_VER > 20
-  test(std::ranges::starts_with, in, in2, binary_pred);
-#endif
-  test(std::ranges::set_difference, in, in2, out, binary_pred);
-  test(std::ranges::set_intersection, in, in2, out, binary_pred);
-  test(std::ranges::set_symmetric_difference, in, in2, out, binary_pred);
-  test(std::ranges::set_union, in, in2, out, binary_pred);
-  test(std::ranges::remove_if, in, unary_pred);
-  test(std::ranges::unique, in, binary_pred);
-  test(std::ranges::partition, in, unary_pred);
-  if (!std::is_constant_evaluated())
-    test(std::ranges::stable_partition, in, unary_pred);
-  test(std::ranges::sort, in, binary_pred);
-  if (!std::is_constant_evaluated())
-    test(std::ranges::stable_sort, in, binary_pred);
-  test_mid(std::ranges::partial_sort, in, mid, binary_pred);
-  test_mid(std::ranges::nth_element, in, mid, binary_pred);
-  if (!std::is_constant_evaluated())
-    test_mid(std::ranges::inplace_merge, in, mid, binary_pred);
-  test(std::ranges::make_heap, in, binary_pred);
-  test(std::ranges::push_heap, in, binary_pred);
-  test(std::ranges::pop_heap, in, binary_pred);
-  test(std::ranges::sort_heap, in, binary_pred);
-  test(std::ranges::prev_permutation, in, binary_pred);
-  test(std::ranges::next_permutation, in, binary_pred);
-
-  return true;
-}
-
-int main(int, char**) {
-  test_all();
-  static_assert(test_all());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/robust_against_nonbool.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/robust_against_nonbool.compile.pass.cpp
new file mode 100644
index 000000000000000..72e209cd39d5e47
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/robust_against_nonbool.compile.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <ranges>
+//
+// Range adaptors that take predicates should support predicates that return a non-boolean
+// value as long as the returned type is implicitly convertible to bool.
+
+#include <ranges>
+
+#include "boolean_testable.h"
+
+template <std::ranges::view View>
+constexpr void use(View view) {
+  // Just use the view in a few ways. Our goal here is to trigger the instantiation
+  // of various functions related to the view and its iterators in the hopes that we
+  // instantiate functions that might have incorrect implementations w.r.t. predicates.
+  auto first = std::ranges::begin(view);
+  auto last  = std::ranges::end(view);
+  ++first;
+  --first;
+  (void)(first == last);
+  (void)(first != last);
+  (void)std::ranges::empty(view);
+}
+
+using Value    = StrictComparable<int>;
+using Iterator = StrictBooleanIterator<Value*>;
+using Range    = std::ranges::subrange<Iterator>;
+auto pred1     = StrictUnaryPredicate;
+auto pred2     = StrictBinaryPredicate;
+
+void f(Range in) {
+  {
+    auto view = std::views::chunk_by(in, pred2);
+    use(view);
+  }
+  {
+    auto view = std::views::drop_while(in, pred1);
+    use(view);
+  }
+  {
+    auto view = std::views::filter(in, pred1);
+    use(view);
+  }
+  {
+    auto view = std::views::take_while(in, pred1);
+    use(view);
+  }
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/ranges_robust_against_nonbool.compile.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/ranges_robust_against_nonbool.compile.pass.cpp
new file mode 100644
index 000000000000000..acfc3513ec7b729
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/ranges_robust_against_nonbool.compile.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+//
+// Make sure we support iterators that return a BooleanTestable in the ranges:: algorithms
+// defined in <memory>.
+
+#include <memory>
+
+#include <ranges>
+
+#include "boolean_testable.h"
+
+using Value    = StrictComparable<int>;
+using Iterator = StrictBooleanIterator<Value*>;
+using Range    = std::ranges::subrange<Iterator>;
+
+void f(Iterator it, Range in, Range out, std::size_t n, Value const& val) {
+  // uninitialized_copy
+  {
+    std::ranges::uninitialized_copy(in, out);
+    std::ranges::uninitialized_copy(it, it, it, it);
+  }
+  // uninitialized_copy_n
+  { std::ranges::uninitialized_copy_n(it, n, it, it); }
+  // uninitialized_fill
+  {
+    std::ranges::uninitialized_fill(it, it, val);
+    std::ranges::uninitialized_fill(in, val);
+  }
+  // uninitialized_fill_n
+  { std::ranges::uninitialized_fill_n(it, n, val); }
+  // uninitialized_move
+  {
+    std::ranges::uninitialized_move(it, it, it, it);
+    std::ranges::uninitialized_move(in, out);
+  }
+  // uninitialized_move_n
+  { std::ranges::uninitialized_move_n(it, n, it, it); }
+  // uninitialized_default_construct
+  {
+    std::ranges::uninitialized_default_construct(it, it);
+    std::ranges::uninitialized_default_construct(in);
+  }
+  // uninitialized_default_construct_n
+  { std::ranges::uninitialized_default_construct_n(it, n); }
+  // uninitialized_value_construct
+  {
+    std::ranges::uninitialized_value_construct(it, it);
+    std::ranges::uninitialized_value_construct(in);
+  }
+  // uninitialized_value_construct_n
+  { std::ranges::uninitialized_value_construct_n(it, n); }
+  // destroy
+  {
+    std::ranges::destroy(it, it);
+    std::ranges::destroy(in);
+  }
+  // destroy_n
+  { std::ranges::destroy_n(it, n); }
+}
diff --git a/libcxx/test/support/boolean_testable.h b/libcxx/test/support/boolean_testable.h
index e810e4e0461dc69..d06511970b9bbec 100644
--- a/libcxx/test/support/boolean_testable.h
+++ b/libcxx/test/support/boolean_testable.h
@@ -11,53 +11,154 @@
 
 #include "test_macros.h"
 
+#include <iterator>
+#include <utility>
+
 #if TEST_STD_VER > 17
 
-class BooleanTestable {
-public:
-  constexpr operator bool() const {
-    return value_;
-  }
+struct BooleanTestable {
+  constexpr operator bool() const { return value_; }
 
   friend constexpr BooleanTestable operator==(const BooleanTestable& lhs, const BooleanTestable& rhs) {
     return lhs.value_ == rhs.value_;
   }
 
   friend constexpr BooleanTestable operator!=(const BooleanTestable& lhs, const BooleanTestable& rhs) {
-    return !(lhs == rhs);
+    return lhs.value_ != rhs.value_;
   }
 
-  constexpr BooleanTestable operator!() {
-    return BooleanTestable{!value_};
+  constexpr BooleanTestable&& operator!() && {
+    value_ = !value_;
+    return std::move(*this);
   }
 
   // this class should behave like a bool, so the constructor shouldn't be explicit
   constexpr BooleanTestable(bool value) : value_{value} {}
   constexpr BooleanTestable(const BooleanTestable&) = delete;
-  constexpr BooleanTestable(BooleanTestable&&) = delete;
+  constexpr BooleanTestable(BooleanTestable&&)      = delete;
 
 private:
   bool value_;
 };
 
+static constexpr BooleanTestable yes(true);
+static constexpr BooleanTestable no(false);
+
 template <class T>
-class StrictComparable {
-public:
+struct StrictComparable {
+  StrictComparable() = default;
+
   // this shouldn't be explicit to make it easier to initlaize inside arrays (which it almost always is)
   constexpr StrictComparable(T value) : value_{value} {}
 
-  friend constexpr BooleanTestable operator==(const StrictComparable& lhs, const StrictComparable& rhs) {
-    return (lhs.value_ == rhs.value_);
+  friend constexpr BooleanTestable const& operator==(StrictComparable const& a, StrictComparable const& b) {
+    return a.value_ == b.value_ ? yes : no;
   }
 
-  friend constexpr BooleanTestable operator!=(const StrictComparable& lhs, const StrictComparable& rhs) {
-    return !(lhs == rhs);
+  friend constexpr BooleanTestable const& operator!=(StrictComparable const& a, StrictComparable const& b) {
+    return a.value_ != b.value_ ? yes : no;
+  }
+
+  friend constexpr BooleanTestable const& operator<(StrictComparable const& a, StrictComparable const& b) {
+    return a.value_ < b.value_ ? yes : no;
+  }
+  friend constexpr BooleanTestable const& operator<=(StrictComparable const& a, StrictComparable const& b) {
+    return a.value_ <= b.value_ ? yes : no;
+  }
+  friend constexpr BooleanTestable const& operator>(StrictComparable const& a, StrictComparable const& b) {
+    return a.value_ > b.value_ ? yes : no;
+  }
+  friend constexpr BooleanTestable const& operator>=(StrictComparable const& a, StrictComparable const& b) {
+    return a.value_ >= b.value_ ? yes : no;
   }
 
-private:
   T value_;
 };
 
+auto StrictUnaryPredicate = []<class T>(StrictComparable<T> const& x) -> BooleanTestable const& {
+  return x.value_ < 0 ? yes : no;
+};
+
+auto StrictBinaryPredicate =
+    []<class T>(StrictComparable<T> const& x, StrictComparable<T> const& y) -> BooleanTestable const& {
+  return x.value_ < y.value_ ? yes : no;
+};
+
+template <class It>
+struct StrictBooleanIterator {
+  using value_type                  = typename std::iterator_traits<It>::value_type;
+  using reference                   = typename std::iterator_traits<It>::reference;
+  using difference_type             = typename std::iterator_traits<It>::difference_type;
+  constexpr StrictBooleanIterator() = default;
+  constexpr explicit StrictBooleanIterator(It it) : iter_(it) {}
+  constexpr reference operator*() const { return *iter_; }
+  constexpr reference operator[](difference_type n) const { return iter_[n]; }
+  constexpr StrictBooleanIterator& operator++() {
+    ++iter_;
+    return *this;
+  }
+  constexpr StrictBooleanIterator operator++(int) {
+    auto copy = *this;
+    ++iter_;
+    return copy;
+  }
+  constexpr StrictBooleanIterator& operator--() {
+    --iter_;
+    return *this;
+  }
+  constexpr StrictBooleanIterator operator--(int) {
+    auto copy = *this;
+    --iter_;
+    return copy;
+  }
+  constexpr StrictBooleanIterator& operator+=(difference_type n) {
+    iter_ += n;
+    return *this;
+  }
+  constexpr StrictBooleanIterator& operator-=(difference_type n) {
+    iter_ -= n;
+    return *this;
+  }
+  friend constexpr StrictBooleanIterator operator+(StrictBooleanIterator x, difference_type n) {
+    x += n;
+    return x;
+  }
+  friend constexpr StrictBooleanIterator operator+(difference_type n, StrictBooleanIterator x) {
+    x += n;
+    return x;
+  }
+  friend constexpr StrictBooleanIterator operator-(StrictBooleanIterator x, difference_type n) {
+    x -= n;
+    return x;
+  }
+  friend constexpr difference_type operator-(StrictBooleanIterator x, StrictBooleanIterator y) {
+    return x.iter_ - y.iter_;
+  }
+  constexpr BooleanTestable const& operator==(StrictBooleanIterator const& other) const {
+    return iter_ == other.iter_ ? yes : no;
+  }
+  constexpr BooleanTestable const& operator!=(StrictBooleanIterator const& other) const {
+    return iter_ != other.iter_ ? yes : no;
+  }
+  constexpr BooleanTestable const& operator<(StrictBooleanIterator const& other) const {
+    return iter_ < other.iter_ ? yes : no;
+  }
+  constexpr BooleanTestable const& operator<=(StrictBooleanIterator const& other) const {
+    return iter_ <= other.iter_ ? yes : no;
+  }
+  constexpr BooleanTestable const& operator>(StrictBooleanIterator const& other) const {
+    return iter_ > other.iter_ ? yes : no;
+  }
+  constexpr BooleanTestable const& operator>=(StrictBooleanIterator const& other) const {
+    return iter_ >= other.iter_ ? yes : no;
+  }
+
+private:
+  It iter_;
+};
+static_assert(std::forward_iterator<StrictBooleanIterator<int*>>);
+static_assert(std::sentinel_for<StrictBooleanIterator<int*>, StrictBooleanIterator<int*>>);
+
 #endif // TEST_STD_VER > 17
 
 #endif // LIBCXX_TEST_SUPPORT_BOOLEAN_TESTABLE_H
diff --git a/libcxx/utils/data/ignore_format.txt b/libcxx/utils/data/ignore_format.txt
index cf6dca7cd7d3c68..b1488d0372ed73a 100644
--- a/libcxx/utils/data/ignore_format.txt
+++ b/libcxx/utils/data/ignore_format.txt
@@ -7217,7 +7217,6 @@ libcxx/test/support/archetypes.h
 libcxx/test/support/archetypes.ipp
 libcxx/test/support/asan_testing.h
 libcxx/test/support/assert_macros.h
-libcxx/test/support/boolean_testable.h
 libcxx/test/support/callable_types.h
 libcxx/test/support/charconv_test_helpers.h
 libcxx/test/support/check_assertion.h

>From 769d480e8739be42d3386991ed989c88f4343744 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Nov 2023 09:27:48 -0400
Subject: [PATCH 2/3] Fix a few tests

---
 .../alg.remove/ranges.remove.pass.cpp              | 14 --------------
 .../alg.replace/ranges.replace.pass.cpp            | 13 -------------
 2 files changed, 27 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove.pass.cpp
index 3f45ef9ab5166e0..523de4cf7b10a83 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.remove/ranges.remove.pass.cpp
@@ -180,20 +180,6 @@ constexpr bool test() {
     }
   }
 
-  {
-    // check that the implicit conversion to bool works
-    {
-      StrictComparable<int> a[] = {1, 2, 3, 4};
-      auto ret                  = std::ranges::remove(a, a + 4, StrictComparable<int>{2});
-      assert(ret.begin() == a + 3);
-    }
-    {
-      StrictComparable<int> a[] = {1, 2, 3, 4};
-      auto ret                  = std::ranges::remove(a, StrictComparable<int>{2});
-      assert(ret.begin() == a + 3);
-    }
-  }
-
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp
index 8721882bbe4e9b8..e682b84f2ff58a8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp
@@ -137,19 +137,6 @@ constexpr bool test() {
     }
   }
 
-  { // check that the implicit conversion to bool works
-    {
-      StrictComparable<int> a[] = {1, 2, 2, 4};
-      auto ret = std::ranges::replace(std::begin(a), std::end(a), 1, 2);
-      assert(ret == std::end(a));
-    }
-    {
-      StrictComparable<int> a[] = {1, 2, 2, 4};
-      auto ret = std::ranges::replace(a, 1, 2);
-      assert(ret == std::end(a));
-    }
-  }
-
   { // check that T1 and T2 can be different types
     {
       StrictComparable<int> a[] = {1, 2, 2, 4};

>From ce832aaf1b17841829cfc4a70c12b002398704bc Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Sun, 5 Nov 2023 09:11:36 -0500
Subject: [PATCH 3/3] Add missing include

---
 .../alg.modifying.operations/alg.replace/ranges.replace.pass.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp
index e682b84f2ff58a8..ab2f705bafe9d02 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.replace/ranges.replace.pass.cpp
@@ -27,6 +27,7 @@
 #include <ranges>
 
 #include "almost_satisfies_types.h"
+#include "boolean_testable.h"
 #include "test_iterators.h"
 
 template <class Iter, class Sent = sentinel_wrapper<Iter>>



More information about the libcxx-commits mailing list