From 6cdd8611e9497604e1beb9a19012773b83fa7cb7 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 17 Oct 2023 13:29:22 -0700 Subject: [PATCH 1/5] [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 ec854763a5a29..3a86701118146 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(__lhs)), std::invoke(__proj2, std::forward(__rhs))); diff --git a/libcxx/include/__algorithm/ranges_find_if_not.h b/libcxx/include/__algorithm/ranges_find_if_not.h index 6beade1462e09..a18bea43165e0 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> _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(__e)); }; + auto __pred2 = [&](auto&& __e) -> bool { return !std::invoke(__pred, std::forward(__e)); }; return ranges::__find_if_impl(std::move(__first), std::move(__last), __pred2, __proj); } template , _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(__e)); }; + auto __pred2 = [&](auto&& __e) -> bool { return !std::invoke(__pred, std::forward(__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 5cc418d3393ce..782ce2670f055 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>) { - 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 2d92661c81099..2ba97042f1f6e 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> _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, _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 bf0928df599ce..e27c4bdd733d8 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, 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, _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 457d593957adb..5158a78e48140 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, 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, _Proj>, const _Type*> _LIBCPP_HIDE_FROM_ABI constexpr remove_copy_result, _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 714fd5c7b089f..b66a41aa8d0d7 100644 --- a/libcxx/include/__algorithm/ranges_replace.h +++ b/libcxx/include/__algorithm/ranges_replace.h @@ -36,7 +36,7 @@ struct __fn { indirect_binary_predicate, 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, _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 124ff8f2c559d..a7627024812fd 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, _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 a12a0e39b0849..7b571fb3448f9 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> _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, _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 108f653753841..96b6a9a997656 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>; - 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>; - 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>; 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>; 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 cfb149b443571..e469986876469 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> { _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](_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> { auto __first = ranges::begin(__base_); reverse_view __reversed{subrange{__first, __current}}; - auto __reversed_pred = [this](_Tp&& __x, _Up&& __y) { + auto __reversed_pred = [this](_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 7836dede7342a..3f45ef9ab5166 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 #include "almost_satisfies_types.h" -#include "boolean_testable.h" #include "test_iterators.h" template > 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 1db4f171b9107..9b8cf1121a13d 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 #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 92ba59c1ddb2b..8721882bbe4e9 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 #include "almost_satisfies_types.h" -#include "boolean_testable.h" #include "test_iterators.h" template > 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 67e5e87af35be..79098b090873c 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 #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 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 d2e0bb5d182d6..7558f6b968132 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 #include "almost_satisfies_types.h" -#include "boolean_testable.h" #include "test_iterators.h" template @@ -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 cea30420428cd..a74e6d721f773 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 #include "almost_satisfies_types.h" -#include "boolean_testable.h" #include "test_iterators.h" template @@ -194,19 +193,6 @@ constexpr bool test() { } } - { // check that the implicit conversion to bool works - StrictComparable a[] = {1, 2, 3, 4}; - StrictComparable 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 a1fb92a984e91..22f938f73ae07 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 #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 3f7ec622f145e..2585feba5614e 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 #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 95860745f5620..171ffefa75cf9 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 #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 5d8086df450ab..6fc6f3ee1bf25 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 #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 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 e190586fef813..9ffde7acc3b65 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 #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 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 331f07755fae4..082de929efae8 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 #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 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 5723ed0d3db25..245d163f984c1 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 #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 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 058be468d35b6..3c03af7db05cd 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 #include "almost_satisfies_types.h" -#include "boolean_testable.h" #include "test_iterators.h" template @@ -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 ad3c8ab699ab4..48816305c8944 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 #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 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 af304662599ca..f348c6e93e19c 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 #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 2463bf733196d..d7bf6dd75f90f 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 #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 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 ea0a3ebdf85b2..c8f76b20c6825 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 #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 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 0000000000000..a5668f20ccfd2 --- /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 + +// +// +// 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 + +#include +#include + +#include "boolean_testable.h" +#include "test_macros.h" + +using Value = StrictComparable; +using Iterator = StrictBooleanIterator; +using Range = std::ranges::subrange; +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 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 04773f0f5bc80..0000000000000 --- 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 - -// -// -// 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 - -#include -#include -#include -#include -#include - -#include "boolean_testable.h" -#include "test_macros.h" - -constexpr auto unary_pred = [](int i) { return BooleanTestable(i > 0); }; -static_assert(!std::same_as); -static_assert(std::convertible_to); - -constexpr auto binary_pred = [](int i, int j) { return BooleanTestable(i < j); }; -static_assert(!std::same_as); -static_assert(std::convertible_to); - -// Invokes both the (iterator, sentinel, ...) and the (range, ...) overloads of the given niebloid. - -// (in, ...) -template -constexpr void test(Func&& func, Input& in, Args&&... args) { - (void)func(in.begin(), in.end(), std::forward(args)...); - (void)func(in, std::forward(args)...); -} - -// (in1, in2, ...) -template -constexpr void test(Func&& func, Input& in1, Input& in2, Args&&... args) { - (void)func(in1.begin(), in1.end(), in2.begin(), in2.end(), std::forward(args)...); - (void)func(in1, in2, std::forward(args)...); -} - -// (in, mid, ...) -template -constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t mid, Args&&... args) { - (void)func(in.begin(), mid, in.end(), std::forward(args)...); - (void)func(in, mid, std::forward(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{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{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{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 0000000000000..72e209cd39d5e --- /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 + +// +// +// 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 + +#include "boolean_testable.h" + +template +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; +using Iterator = StrictBooleanIterator; +using Range = std::ranges::subrange; +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 0000000000000..acfc3513ec7b7 --- /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 + +// +// +// Make sure we support iterators that return a BooleanTestable in the ranges:: algorithms +// defined in . + +#include + +#include + +#include "boolean_testable.h" + +using Value = StrictComparable; +using Iterator = StrictBooleanIterator; +using Range = std::ranges::subrange; + +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 e810e4e0461dc..d06511970b9bb 100644 --- a/libcxx/test/support/boolean_testable.h +++ b/libcxx/test/support/boolean_testable.h @@ -11,53 +11,154 @@ #include "test_macros.h" +#include +#include + #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 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 = [](StrictComparable const& x) -> BooleanTestable const& { + return x.value_ < 0 ? yes : no; +}; + +auto StrictBinaryPredicate = + [](StrictComparable const& x, StrictComparable const& y) -> BooleanTestable const& { + return x.value_ < y.value_ ? yes : no; +}; + +template +struct StrictBooleanIterator { + using value_type = typename std::iterator_traits::value_type; + using reference = typename std::iterator_traits::reference; + using difference_type = typename std::iterator_traits::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>); +static_assert(std::sentinel_for, StrictBooleanIterator>); + #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 cf6dca7cd7d3c..b1488d0372ed7 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 Date: Thu, 2 Nov 2023 09:27:48 -0400 Subject: [PATCH 2/5] 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 3f45ef9ab5166..523de4cf7b10a 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 a[] = {1, 2, 3, 4}; - auto ret = std::ranges::remove(a, a + 4, StrictComparable{2}); - assert(ret.begin() == a + 3); - } - { - StrictComparable a[] = {1, 2, 3, 4}; - auto ret = std::ranges::remove(a, StrictComparable{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 8721882bbe4e9..e682b84f2ff58 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 a[] = {1, 2, 2, 4}; - auto ret = std::ranges::replace(std::begin(a), std::end(a), 1, 2); - assert(ret == std::end(a)); - } - { - StrictComparable 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 a[] = {1, 2, 2, 4}; From ce832aaf1b17841829cfc4a70c12b002398704bc Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Sun, 5 Nov 2023 09:11:36 -0500 Subject: [PATCH 3/5] 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 e682b84f2ff58..ab2f705bafe9d 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 #include "almost_satisfies_types.h" +#include "boolean_testable.h" #include "test_iterators.h" template > From fe3b59f8a2536add65f7a500951c22324a679496 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Sun, 5 Nov 2023 16:33:12 -0700 Subject: [PATCH 4/5] Fix C++20 test --- .../range.adaptors/robust_against_nonbool.compile.pass.cpp | 4 ++++ 1 file changed, 4 insertions(+) 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 index 72e209cd39d5e..ab80456c21efd 100644 --- 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 @@ -38,10 +38,14 @@ auto pred1 = StrictUnaryPredicate; auto pred2 = StrictBinaryPredicate; void f(Range in) { + (void)pred1; (void)pred2; + +#if TEST_STD_VER >= 23 { auto view = std::views::chunk_by(in, pred2); use(view); } +#endif { auto view = std::views::drop_while(in, pred1); use(view); From 050d229147ac13f7a947247486c8ec52cc75b536 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 6 Nov 2023 09:29:59 -1000 Subject: [PATCH 5/5] Fix formatting --- .../range.adaptors/robust_against_nonbool.compile.pass.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index ab80456c21efd..5cd20bce610c7 100644 --- 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 @@ -38,7 +38,8 @@ auto pred1 = StrictUnaryPredicate; auto pred2 = StrictBinaryPredicate; void f(Range in) { - (void)pred1; (void)pred2; + (void)pred1; + (void)pred2; #if TEST_STD_VER >= 23 {