From 623e18e5d4f8050132b028c183f0a1c1be80f5d6 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sun, 5 Jul 2020 21:25:32 +0200 Subject: [PATCH 1/8] Implement ranges::remove family --- stl/inc/algorithm | 299 ++++++++++++++++-- tests/std/include/range_algorithm_support.hpp | 5 + tests/std/test.lst | 4 + .../tests/P0896R4_ranges_alg_remove/env.lst | 4 + .../tests/P0896R4_ranges_alg_remove/test.cpp | 51 +++ .../P0896R4_ranges_alg_remove_copy/env.lst | 4 + .../P0896R4_ranges_alg_remove_copy/test.cpp | 61 ++++ .../P0896R4_ranges_alg_remove_copy_if/env.lst | 4 + .../test.cpp | 64 ++++ .../P0896R4_ranges_alg_remove_if/env.lst | 4 + .../P0896R4_ranges_alg_remove_if/test.cpp | 53 ++++ 11 files changed, 532 insertions(+), 21 deletions(-) create mode 100644 tests/std/tests/P0896R4_ranges_alg_remove/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_remove/test.cpp create mode 100644 tests/std/tests/P0896R4_ranges_alg_remove_copy/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp create mode 100644 tests/std/tests/P0896R4_ranges_alg_remove_copy_if/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp create mode 100644 tests/std/tests/P0896R4_ranges_alg_remove_if/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 945936ee546..f6a72f84140 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -386,6 +386,20 @@ namespace ranges { inline constexpr _For_each_n_fn for_each_n{_Not_quite_object::_Construct_tag{}}; // VARIABLE ranges::find + // clang-format off + template _Se, class _Ty, class _Pj = identity> + requires indirect_binary_predicate, const _Ty*> + _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj = {}) { + for (; _First != _Last; ++_First) { + if (_STD invoke(_Proj, *_First) == _Val) { + break; + } + } + + return _First; + } + // clang-format on + class _Find_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -395,16 +409,10 @@ namespace ranges { requires indirect_binary_predicate, const _Ty*> _NODISCARD constexpr _It operator()(_It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); - // Performance note: investigate using _Find_unchecked when same_as<_Pj, identity> - auto _UFirst = _Get_unwrapped(_STD move(_First)); - const auto _ULast = _Get_unwrapped(_STD move(_Last)); - for (; _UFirst != _ULast; ++_UFirst) { - if (_STD invoke(_Proj, *_UFirst) == _Val) { - break; - } - } + auto _UResult = _RANGES _Find_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Val, _Pass_fn(_Proj)); - _Seek_wrapped(_First, _STD move(_UFirst)); + _Seek_wrapped(_First, _STD move(_UResult)); return _First; } @@ -412,7 +420,11 @@ namespace ranges { requires indirect_binary_predicate, _Pj>, const _Ty*> _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()( _Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { - return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Val, _Pass_fn(_Proj)); + auto _First = _RANGES begin(_Range); + auto _UResult = _RANGES _Find_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _Val, _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; } // clang-format on }; @@ -430,6 +442,18 @@ _NODISCARD _FwdIt find_if(_ExPo&& _Exec, _FwdIt _First, const _FwdIt _Last, _Pr #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::find_if + template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr _It _Find_if_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj = {}) { + for (; _First != _Last; ++_First) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + break; + } + } + + return _First; + } + class _Find_if_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -438,22 +462,22 @@ namespace ranges { indirect_unary_predicate> _Pr> _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_STD move(_First)); - const auto _ULast = _Get_unwrapped(_STD move(_Last)); - for (; _UFirst != _ULast; ++_UFirst) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst))) { - break; - } - } + auto _UResult = _RANGES _Find_if_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); - _Seek_wrapped(_First, _STD move(_UFirst)); + _Seek_wrapped(_First, _STD move(_UResult)); return _First; } template , _Pj>> _Pr> _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + auto _First = _RANGES begin(_Range); + auto _UResult = _RANGES _Find_if_unchecked( + _Get_unwrapped(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; } }; @@ -3871,13 +3895,246 @@ _FwdIt2 remove_copy_if(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Last, _FwdIt2 _Dest, _P template = 0> _NODISCARD _FwdIt remove( _ExPo&& _Exec, const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) noexcept; // terminates -#endif // _HAS_CXX17 -#if _HAS_CXX17 template = 0> _NODISCARD _FwdIt remove_if(_ExPo&& _Exec, _FwdIt _First, const _FwdIt _Last, _Pr _Pred) noexcept; // terminates #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::remove + class _Remove_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Ty, class _Pj = identity> + requires indirect_binary_predicate, const _Ty*> + constexpr subrange<_It> operator()(_It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _Remove_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Val, _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } + + template + requires permutable> + && indirect_binary_predicate, _Pj>, const _Ty*> + constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { + auto _UResult = _Remove_unchecked(_Ubegin(_Range), _Uend(_Range), _Val, _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } + // clang-format on + private: + template + _NODISCARD static constexpr subrange<_It> _Remove_unchecked( + _It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj) { + // Remove projected values equal to _Val from [_First, _Last) + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_binary_predicate, const _Ty*>); + + _First = _RANGES _Find_unchecked(_STD move(_First), _Last, _Val, _Proj); + auto _Next = _First; + if (_First == _Last) { + return {_STD move(_Next), _STD move(_First)}; + } + + while (++_First != _Last) { + if (_STD invoke(_Proj, *_First) != _Val) { + *_Next = _RANGES iter_move(_First); + ++_Next; + } + } + + return {_STD move(_Next), _STD move(_First)}; + } + }; + + inline constexpr _Remove_fn remove{_Not_quite_object::_Construct_tag{}}; + + // VARIABLE ranges::remove_if + class _Remove_if_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + constexpr subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _Remove_if_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } + + // clang-format off + template , _Pj>> _Pr> + requires permutable> + constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + auto _UResult = _Remove_if_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } + // clang-format on + private: + template + _NODISCARD static constexpr subrange<_It> _Remove_if_unchecked( + _It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + // Remove values whose projection satisfies _Pred from [_First, _Last) + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + _First = _RANGES _Find_if_unchecked(_STD move(_First), _Last, _Pred, _Proj); + auto _Next = _First; + if (_First == _Last) { + return {_STD move(_Next), _STD move(_First)}; + } + + while (++_First != _Last) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + *_Next = _RANGES iter_move(_First); + ++_Next; + } + } + + return {_STD move(_Next), _STD move(_First)}; + } + }; + + inline constexpr _Remove_if_fn remove_if{_Not_quite_object::_Construct_tag{}}; + + // ALIAS TEMPLATE remove_copy_result + template + using remove_copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::remove_copy + class _Remove_copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out, class _Ty, class _Pj = identity> + requires indirectly_copyable<_It, _Out> + && indirect_binary_predicate, const _Ty*> + constexpr remove_copy_result<_It, _Out> operator()( + _It _First, _Se _Last, _Out _Result, const _Ty& _Val, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _Remove_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), + _Get_unwrapped_unverified(_STD move(_Result)), _Val, _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + + template + requires indirectly_copyable, _Out> + && indirect_binary_predicate, _Pj>, const _Ty*> + constexpr remove_copy_result, _Out> operator()( + _Rng&& _Range, _Out _Result, const _Ty& _Val, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto _UResult = _Remove_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), + _Get_unwrapped_unverified(_STD move(_Result)), _Val, _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr remove_copy_result<_It, _Out> _Remove_copy_unchecked( + _It _First, const _Se _Last, _Out _Result, const _Ty& _Val, _Pj _Proj) { + // Copy [_First, _Last) to _Result except projected values equal to _Val + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + _STL_INTERNAL_STATIC_ASSERT(indirect_binary_predicate, const _Ty*>); + + for (; _First != _Last; ++_First) { + if (_STD invoke(_Proj, *_First) != _Val) { + *_Result = *_First; + ++_Result; + } + } + + return {_STD move(_First), _STD move(_Result)}; + } + }; + + inline constexpr _Remove_copy_fn remove_copy{_Not_quite_object::_Construct_tag{}}; + + // ALIAS TEMPLATE remove_copy_if_result + template + using remove_copy_if_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::remove_copy_if + class _Remove_copy_if_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out, class _Pj = identity, + indirect_unary_predicate> _Pr> + requires indirectly_copyable<_It, _Out> + constexpr remove_copy_if_result<_It, _Out> operator()( + _It _First, _Se _Last, _Out _Result, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = + _Remove_copy_if_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + + template , _Pj>> _Pr> + requires indirectly_copyable, _Out> + constexpr remove_copy_if_result, _Out> operator()( + _Rng&& _Range, _Out _Result, _Pr _Pred, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto _UResult = _Remove_copy_if_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr remove_copy_if_result<_It, _Out> _Remove_copy_if_unchecked( + _It _First, const _Se _Last, _Out _Result, _Pr _Pred, _Pj _Proj) { + // Copy [_First, _Last) to _Result except projected values that satisfy _Pred + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + for (; _First != _Last; ++_First) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + *_Result = *_First; + ++_Result; + } + } + + return {_STD move(_First), _STD move(_Result)}; + } + }; + + inline constexpr _Remove_copy_if_fn remove_copy_if{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE unique template _NODISCARD _CONSTEXPR20 _FwdIt unique(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index d5fa87ddd08..9727362aace 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -1008,6 +1008,11 @@ constexpr void test_in_write() { with_input_ranges, Element1>::call(); } +template +constexpr void test_fwd_write() { + with_forward_ranges, Element1>::call(); +} + template constexpr void test_read() { with_input_iterators::call(); diff --git a/tests/std/test.lst b/tests/std/test.lst index 4c9d6bf510c..ac1d9478867 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -266,6 +266,10 @@ tests\P0896R4_ranges_alg_none_of tests\P0896R4_ranges_alg_partition tests\P0896R4_ranges_alg_partition_copy tests\P0896R4_ranges_alg_partition_point +tests\P0896R4_ranges_alg_remove +tests\P0896R4_ranges_alg_remove_copy +tests\P0896R4_ranges_alg_remove_copy_if +tests\P0896R4_ranges_alg_remove_if tests\P0896R4_ranges_alg_replace tests\P0896R4_ranges_alg_replace_copy tests\P0896R4_ranges_alg_replace_copy_if diff --git a/tests/std/tests/P0896R4_ranges_alg_remove/env.lst b/tests/std/tests/P0896R4_ranges_alg_remove/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp new file mode 100644 index 00000000000..29f3d312ae6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +// Validate dangling story +STATIC_ASSERT(same_as{}, 42)), ranges::dangling>); +STATIC_ASSERT(same_as{}, 42)), ranges::subrange>); + +struct instantiator { + static constexpr P expected[3] = {{0, 99}, {2, 99}, {4, 99}}; + + template + static constexpr void call() { + using ranges::remove, ranges::subrange, ranges::iterator_t; + { // Validate iterator + sentinel overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; + + auto result = remove(wrapped_input.begin(), wrapped_input.end(), 47, get_second); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(ranges::equal(expected, span{input}.first<3>())); + } + { // Validate range overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; + + auto result = remove(wrapped_input, 47, get_second); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(ranges::equal(expected, span{input}.first<3>())); + } + } +}; + +int main() { + STATIC_ASSERT((test_fwd(), true)); + test_fwd(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy/env.lst b/tests/std/tests/P0896R4_ranges_alg_remove_copy/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp new file mode 100644 index 00000000000..8b1d8fa4619 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +// Validate that remove_copy_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, static_cast(nullptr), 42)), + ranges::remove_copy_result>); +STATIC_ASSERT(same_as{}, static_cast(nullptr), 42)), + ranges::remove_copy_result>); + +struct instantiator { + static constexpr P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + static constexpr P expected[3] = {{0, 99}, {2, 99}, {4, 99}}; + + static constexpr void eq(P const (&output)[3]) { + // Extracted into a separate function to keep /analyze from exhausting the compiler heap + assert(ranges::equal(output, expected)); + } + + template > Write> + static constexpr void call() { + using ranges::remove_copy, ranges::remove_copy_result, ranges::iterator_t; + { // Validate iterator + sentinel overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + + auto result = remove_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, 47, get_second); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + eq(output); + } + { // Validate range overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + + auto result = remove_copy(wrapped_input, Write{output}, 47, get_second); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + eq(output); + } + } +}; + +int main() { + STATIC_ASSERT((test_in_write(), true)); + test_in_write(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/env.lst b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp new file mode 100644 index 00000000000..2486e7292bc --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +constexpr auto matches = [](const int val) { return val == 47; }; + +// Validate that remove_copy_if_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, static_cast(nullptr), matches)), + ranges::remove_copy_if_result>); +STATIC_ASSERT(same_as{}, static_cast(nullptr), matches)), + ranges::remove_copy_if_result>); + +struct instantiator { + static constexpr P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + static constexpr P expected[3] = {{0, 99}, {2, 99}, {4, 99}}; + + static constexpr void eq(P const (&output)[3]) { + // Extracted into a separate function to keep /analyze from exhausting the compiler heap + assert(ranges::equal(output, expected)); + } + + template > Write> + static constexpr void call() { + using ranges::remove_copy_if, ranges::remove_copy_if_result, ranges::iterator_t; + { // Validate iterator + sentinel overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + + auto result = + remove_copy_if(wrapped_input.begin(), wrapped_input.end(), Write{output}, matches, get_second); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + eq(output); + } + { // Validate range overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + + auto result = remove_copy_if(wrapped_input, Write{output}, matches, get_second); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + eq(output); + } + } +}; + +int main() { + STATIC_ASSERT((test_in_write(), true)); + test_in_write(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_if/env.lst b/tests/std/tests/P0896R4_ranges_alg_remove_if/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_if/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp new file mode 100644 index 00000000000..a35b6c480f4 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +constexpr auto matches = [](int const val) { return val == 47; }; + +// Validate dangling story +STATIC_ASSERT(same_as{}, matches)), ranges::dangling>); +STATIC_ASSERT(same_as{}, matches)), ranges::subrange>); + +struct instantiator { + static constexpr P expected[3] = {{0, 99}, {2, 99}, {4, 99}}; + + template + static constexpr void call() { + using ranges::remove_if, ranges::subrange, ranges::iterator_t; + { // Validate iterator + sentinel overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; + + auto result = remove_if(wrapped_input.begin(), wrapped_input.end(), matches, get_second); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(ranges::equal(expected, span{input}.first<3>())); + } + { // Validate range overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; + + auto result = remove_if(wrapped_input, matches, get_second); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(ranges::equal(expected, span{input}.first<3>())); + } + } +}; + +int main() { + STATIC_ASSERT((test_fwd(), true)); + test_fwd(); +} From 0d1f9ff27487fb83257e8d1ba596e1d2df72f818 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 11 Jul 2020 21:28:51 +0200 Subject: [PATCH 2/8] Address review comments --- stl/inc/algorithm | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index f6a72f84140..308e1271bd5 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -386,10 +386,11 @@ namespace ranges { inline constexpr _For_each_n_fn for_each_n{_Not_quite_object::_Construct_tag{}}; // VARIABLE ranges::find + // concept-constrained for strict enforcement as it is used by several algorithms. // clang-format off - template _Se, class _Ty, class _Pj = identity> + template _Se, class _Ty, class _Pj> requires indirect_binary_predicate, const _Ty*> - _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj = {}) { + _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj) { for (; _First != _Last; ++_First) { if (_STD invoke(_Proj, *_First) == _Val) { break; @@ -420,8 +421,9 @@ namespace ranges { requires indirect_binary_predicate, _Pj>, const _Ty*> _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()( _Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - auto _UResult = _RANGES _Find_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _Val, _Pass_fn(_Proj)); + auto _First = _RANGES begin(_Range); + auto _UResult = + _RANGES _Find_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _Val, _Pass_fn(_Proj)); _Seek_wrapped(_First, _STD move(_UResult)); return _First; @@ -442,9 +444,9 @@ _NODISCARD _FwdIt find_if(_ExPo&& _Exec, _FwdIt _First, const _FwdIt _Last, _Pr #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::find_if - template _Se, class _Pj = identity, - indirect_unary_predicate> _Pr> - _NODISCARD constexpr _It _Find_if_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj = {}) { + // concept-constrained for strict enforcement as it is used by several algorithms. + template _Se, class _Pj, indirect_unary_predicate> _Pr> + _NODISCARD constexpr _It _Find_if_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { for (; _First != _Last; ++_First) { if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { break; @@ -3910,7 +3912,7 @@ namespace ranges { // clang-format off template _Se, class _Ty, class _Pj = identity> requires indirect_binary_predicate, const _Ty*> - constexpr subrange<_It> operator()(_It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const { + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); auto _UResult = _Remove_unchecked( _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Val, _Pass_fn(_Proj)); @@ -3921,7 +3923,8 @@ namespace ranges { template requires permutable> && indirect_binary_predicate, _Pj>, const _Ty*> - constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { + _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()( + _Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { auto _UResult = _Remove_unchecked(_Ubegin(_Range), _Uend(_Range), _Val, _Pass_fn(_Proj)); return _Rewrap_subrange>(_Range, _STD move(_UResult)); @@ -3962,7 +3965,7 @@ namespace ranges { template _Se, class _Pj = identity, indirect_unary_predicate> _Pr> - constexpr subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); auto _UResult = _Remove_if_unchecked( _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); @@ -3974,7 +3977,7 @@ namespace ranges { template , _Pj>> _Pr> requires permutable> - constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { auto _UResult = _Remove_if_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); return _Rewrap_subrange>(_Range, _STD move(_UResult)); From fe5e859f97278487693e96ae2fe4dec1c05e1430 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 11 Jul 2020 21:46:16 +0200 Subject: [PATCH 3/8] Ensure that we only do N comparisons --- .../std/tests/P0896R4_ranges_alg_remove/test.cpp | 16 ++++++++++++++-- .../P0896R4_ranges_alg_remove_copy/test.cpp | 16 ++++++++++++++-- .../P0896R4_ranges_alg_remove_copy_if/test.cpp | 16 ++++++++++++++-- .../tests/P0896R4_ranges_alg_remove_if/test.cpp | 16 ++++++++++++++-- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp index 29f3d312ae6..b7e19d54375 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp @@ -25,22 +25,34 @@ struct instantiator { { // Validate iterator + sentinel overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = remove(wrapped_input.begin(), wrapped_input.end(), 47, get_second); + auto result = remove(wrapped_input.begin(), wrapped_input.end(), 47, projection); STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); assert(ranges::equal(expected, span{input}.first<3>())); + assert(comparisonsCounter == ranges::size(input)); } { // Validate range overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = remove(wrapped_input, 47, get_second); + auto result = remove(wrapped_input, 47, projection); STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); assert(ranges::equal(expected, span{input}.first<3>())); + assert(comparisonsCounter == ranges::size(input)); } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp index 8b1d8fa4619..c838788d41b 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp @@ -35,22 +35,34 @@ struct instantiator { { // Validate iterator + sentinel overload P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = remove_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, 47, get_second); + auto result = remove_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, 47, projection); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 3); eq(output); + assert(comparisonsCounter == ranges::size(input)); } { // Validate range overload P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = remove_copy(wrapped_input, Write{output}, 47, get_second); + auto result = remove_copy(wrapped_input, Write{output}, 47, projection); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 3); eq(output); + assert(comparisonsCounter == ranges::size(input)); } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp index 2486e7292bc..7c2b229f010 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp @@ -37,23 +37,35 @@ struct instantiator { { // Validate iterator + sentinel overload P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; auto result = - remove_copy_if(wrapped_input.begin(), wrapped_input.end(), Write{output}, matches, get_second); + remove_copy_if(wrapped_input.begin(), wrapped_input.end(), Write{output}, matches, projection); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 3); eq(output); + assert(comparisonsCounter == ranges::size(input)); } { // Validate range overload P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = remove_copy_if(wrapped_input, Write{output}, matches, get_second); + auto result = remove_copy_if(wrapped_input, Write{output}, matches, projection); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 3); eq(output); + assert(comparisonsCounter == ranges::size(input)); } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp index a35b6c480f4..7e2a8ec87fe 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp @@ -27,22 +27,34 @@ struct instantiator { { // Validate iterator + sentinel overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = remove_if(wrapped_input.begin(), wrapped_input.end(), matches, get_second); + auto result = remove_if(wrapped_input.begin(), wrapped_input.end(), matches, projection); STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); assert(ranges::equal(expected, span{input}.first<3>())); + assert(comparisonsCounter == ranges::size(input)); } { // Validate range overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = remove_if(wrapped_input, matches, get_second); + auto result = remove_if(wrapped_input, matches, projection); STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); assert(ranges::equal(expected, span{input}.first<3>())); + assert(comparisonsCounter == ranges::size(input)); } } }; From cc64c086c2fbc5a22e2da417e05ad590fa2f6e58 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sun, 12 Jul 2020 20:37:24 +0200 Subject: [PATCH 4/8] Apply fix against VSO-938163 --- .../tests/P0896R4_ranges_alg_remove/test.cpp | 6 +- .../P0896R4_ranges_alg_remove_copy/test.cpp | 73 +++++++++--------- .../test.cpp | 75 ++++++++++--------- .../P0896R4_ranges_alg_remove_if/test.cpp | 6 +- 4 files changed, 83 insertions(+), 77 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp index b7e19d54375..1c3e8402d1d 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp @@ -21,7 +21,7 @@ struct instantiator { template static constexpr void call() { - using ranges::remove, ranges::subrange, ranges::iterator_t; + using ranges::remove, ranges::subrange, ranges::equal, ranges::iterator_t; { // Validate iterator + sentinel overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; Read wrapped_input{input}; @@ -35,7 +35,7 @@ struct instantiator { STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); - assert(ranges::equal(expected, span{input}.first<3>())); + assert(equal(expected, span{input}.first<3>())); assert(comparisonsCounter == ranges::size(input)); } { // Validate range overload @@ -51,7 +51,7 @@ struct instantiator { STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); - assert(ranges::equal(expected, span{input}.first<3>())); + assert(equal(expected, span{input}.first<3>())); assert(comparisonsCounter == ranges::size(input)); } } diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp index c838788d41b..3b08edba9a2 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp @@ -24,50 +24,53 @@ struct instantiator { static constexpr P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; static constexpr P expected[3] = {{0, 99}, {2, 99}, {4, 99}}; - static constexpr void eq(P const (&output)[3]) { - // Extracted into a separate function to keep /analyze from exhausting the compiler heap - assert(ranges::equal(output, expected)); - } - template > Write> static constexpr void call() { - using ranges::remove_copy, ranges::remove_copy_result, ranges::iterator_t; - { // Validate iterator + sentinel overload - P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; - Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 +#pragma warning(suppress : 4127) // conditional expression is constant + if (!ranges::contiguous_range || !is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using ranges::remove_copy, ranges::remove_copy_result, ranges::equal, ranges::iterator_t; + { // Validate iterator + sentinel overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = remove_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, 47, projection); - STATIC_ASSERT(same_as, Write>>); - assert(result.in == wrapped_input.end()); - assert(result.out.peek() == output + 3); - eq(output); - assert(comparisonsCounter == ranges::size(input)); - } - { // Validate range overload - P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; - Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; + auto result = remove_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, 47, projection); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + assert(equal(output, expected)); + assert(comparisonsCounter == ranges::size(input)); + } + { // Validate range overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = remove_copy(wrapped_input, Write{output}, 47, projection); - STATIC_ASSERT(same_as, Write>>); - assert(result.in == wrapped_input.end()); - assert(result.out.peek() == output + 3); - eq(output); - assert(comparisonsCounter == ranges::size(input)); + auto result = remove_copy(wrapped_input, Write{output}, 47, projection); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + assert(equal(output, expected)); + assert(comparisonsCounter == ranges::size(input)); + } } } }; int main() { +#ifndef _PREFAST_ // TRANSITION, GH-1030 STATIC_ASSERT((test_in_write(), true)); +#endif // TRANSITION, GH-1030 test_in_write(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp index 7c2b229f010..790a894dc29 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp @@ -26,51 +26,54 @@ struct instantiator { static constexpr P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; static constexpr P expected[3] = {{0, 99}, {2, 99}, {4, 99}}; - static constexpr void eq(P const (&output)[3]) { - // Extracted into a separate function to keep /analyze from exhausting the compiler heap - assert(ranges::equal(output, expected)); - } - template > Write> static constexpr void call() { - using ranges::remove_copy_if, ranges::remove_copy_if_result, ranges::iterator_t; - { // Validate iterator + sentinel overload - P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; - Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 +#pragma warning(suppress : 4127) // conditional expression is constant + if (!ranges::contiguous_range || !is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using ranges::remove_copy_if, ranges::remove_copy_if_result, ranges::equal, ranges::iterator_t; + { // Validate iterator + sentinel overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = - remove_copy_if(wrapped_input.begin(), wrapped_input.end(), Write{output}, matches, projection); - STATIC_ASSERT(same_as, Write>>); - assert(result.in == wrapped_input.end()); - assert(result.out.peek() == output + 3); - eq(output); - assert(comparisonsCounter == ranges::size(input)); - } - { // Validate range overload - P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; - Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; + auto result = + remove_copy_if(wrapped_input.begin(), wrapped_input.end(), Write{output}, matches, projection); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + assert(equal(output, expected)); + assert(comparisonsCounter == ranges::size(input)); + } + { // Validate range overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; - auto result = remove_copy_if(wrapped_input, Write{output}, matches, projection); - STATIC_ASSERT(same_as, Write>>); - assert(result.in == wrapped_input.end()); - assert(result.out.peek() == output + 3); - eq(output); - assert(comparisonsCounter == ranges::size(input)); + auto result = remove_copy_if(wrapped_input, Write{output}, matches, projection); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + assert(equal(output, expected)); + assert(comparisonsCounter == ranges::size(input)); + } } } }; int main() { +#ifndef _PREFAST_ // TRANSITION, GH-1030 STATIC_ASSERT((test_in_write(), true)); +#endif // TRANSITION, GH-1030 test_in_write(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp index 7e2a8ec87fe..a993ec0b33f 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp @@ -23,7 +23,7 @@ struct instantiator { template static constexpr void call() { - using ranges::remove_if, ranges::subrange, ranges::iterator_t; + using ranges::remove_if, ranges::subrange, ranges::equal, ranges::iterator_t; { // Validate iterator + sentinel overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; Read wrapped_input{input}; @@ -37,7 +37,7 @@ struct instantiator { STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); - assert(ranges::equal(expected, span{input}.first<3>())); + assert(equal(expected, span{input}.first<3>())); assert(comparisonsCounter == ranges::size(input)); } { // Validate range overload @@ -53,7 +53,7 @@ struct instantiator { STATIC_ASSERT(same_as>>); assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); - assert(ranges::equal(expected, span{input}.first<3>())); + assert(equal(expected, span{input}.first<3>())); assert(comparisonsCounter == ranges::size(input)); } } From aaccfc127450b3a35fa4ce3c53372b089f4a0236 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 12 Jul 2020 19:34:22 -0700 Subject: [PATCH 5/8] Review comments and test failures: * Reuse the projection that counts calls rather than duplicating to reduce the total number of template instantiations. * `remove_copy`/`remove_copy_if` failures are caused by proxy iterators in permissive mode - which we can't support anyway - not by VSO-938163. --- .../tests/P0896R4_ranges_alg_remove/test.cpp | 20 ++++++------ .../P0896R4_ranges_alg_remove_copy/test.cpp | 30 ++++++++--------- .../test.cpp | 32 +++++++++---------- .../P0896R4_ranges_alg_remove_if/test.cpp | 20 ++++++------ 4 files changed, 50 insertions(+), 52 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp index 1c3e8402d1d..1e3d0a78c9c 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp @@ -22,14 +22,16 @@ struct instantiator { template static constexpr void call() { using ranges::remove, ranges::subrange, ranges::equal, ranges::iterator_t; + + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; + { // Validate iterator + sentinel overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; auto result = remove(wrapped_input.begin(), wrapped_input.end(), 47, projection); STATIC_ASSERT(same_as>>); @@ -38,14 +40,12 @@ struct instantiator { assert(equal(expected, span{input}.first<3>())); assert(comparisonsCounter == ranges::size(input)); } + + comparisonsCounter = 0; + { // Validate range overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; auto result = remove(wrapped_input, 47, projection); STATIC_ASSERT(same_as>>); diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp index 3b08edba9a2..90b837e05cf 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp @@ -26,20 +26,22 @@ struct instantiator { template > Write> static constexpr void call() { -#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 -#pragma warning(suppress : 4127) // conditional expression is constant - if (!ranges::contiguous_range || !is_constant_evaluated()) -#endif // TRANSITION, VSO-938163 - { + // Fails checking the indirect_binary_predicate requirement in C1XX's permissive mode with proxy iterators + // (probably related to VSO-566808) + constexpr bool non_proxy = + is_reference_v> && is_reference_v>; + if constexpr (non_proxy || !is_permissive) { using ranges::remove_copy, ranges::remove_copy_result, ranges::equal, ranges::iterator_t; + + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; + { // Validate iterator + sentinel overload P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; auto result = remove_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, 47, projection); STATIC_ASSERT(same_as, Write>>); @@ -48,14 +50,12 @@ struct instantiator { assert(equal(output, expected)); assert(comparisonsCounter == ranges::size(input)); } + + comparisonsCounter = 0; + { // Validate range overload P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; auto result = remove_copy(wrapped_input, Write{output}, 47, projection); STATIC_ASSERT(same_as, Write>>); diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp index 790a894dc29..c38be85b257 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp @@ -28,21 +28,22 @@ struct instantiator { template > Write> static constexpr void call() { -#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 -#pragma warning(suppress : 4127) // conditional expression is constant - if (!ranges::contiguous_range || !is_constant_evaluated()) -#endif // TRANSITION, VSO-938163 - { + // Fails checking the indirect_binary_predicate requirement in C1XX's permissive mode with proxy iterators + // (probably related to VSO-566808) + constexpr bool non_proxy = + is_reference_v> && is_reference_v>; + if constexpr (non_proxy || !is_permissive) { using ranges::remove_copy_if, ranges::remove_copy_if_result, ranges::equal, ranges::iterator_t; + + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; + { // Validate iterator + sentinel overload P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; - auto result = remove_copy_if(wrapped_input.begin(), wrapped_input.end(), Write{output}, matches, projection); STATIC_ASSERT(same_as, Write>>); @@ -51,15 +52,12 @@ struct instantiator { assert(equal(output, expected)); assert(comparisonsCounter == ranges::size(input)); } + + comparisonsCounter = 0; + { // Validate range overload P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; - auto result = remove_copy_if(wrapped_input, Write{output}, matches, projection); STATIC_ASSERT(same_as, Write>>); assert(result.in == wrapped_input.end()); diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp index a993ec0b33f..10e443c9adf 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp @@ -24,14 +24,16 @@ struct instantiator { template static constexpr void call() { using ranges::remove_if, ranges::subrange, ranges::equal, ranges::iterator_t; + + size_t comparisonsCounter = 0; + auto projection = [&comparisonsCounter](const P& data) { + ++comparisonsCounter; + return data.second; + }; + { // Validate iterator + sentinel overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; auto result = remove_if(wrapped_input.begin(), wrapped_input.end(), matches, projection); STATIC_ASSERT(same_as>>); @@ -40,14 +42,12 @@ struct instantiator { assert(equal(expected, span{input}.first<3>())); assert(comparisonsCounter == ranges::size(input)); } + + comparisonsCounter = 0; + { // Validate range overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; Read wrapped_input{input}; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; - }; auto result = remove_if(wrapped_input, matches, projection); STATIC_ASSERT(same_as>>); From dc551d1076405e93e58405a66a0e17a8c29b7ae6 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 13 Jul 2020 12:32:56 +0200 Subject: [PATCH 6/8] Review comments from STL --- .../std/tests/P0896R4_ranges_alg_remove/test.cpp | 14 +++++++------- .../P0896R4_ranges_alg_remove_copy/test.cpp | 14 +++++++------- .../P0896R4_ranges_alg_remove_copy_if/test.cpp | 14 +++++++------- .../tests/P0896R4_ranges_alg_remove_if/test.cpp | 16 ++++++++-------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp index 1e3d0a78c9c..769c64d6ab4 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp @@ -23,10 +23,10 @@ struct instantiator { static constexpr void call() { using ranges::remove, ranges::subrange, ranges::equal, ranges::iterator_t; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; + size_t projectionCounter = 0; + auto projection = [&projectionCounter](const P& val) { + ++projectionCounter; + return val.second; }; { // Validate iterator + sentinel overload @@ -38,10 +38,10 @@ struct instantiator { assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); assert(equal(expected, span{input}.first<3>())); - assert(comparisonsCounter == ranges::size(input)); + assert(projectionCounter == ranges::size(input)); } - comparisonsCounter = 0; + projectionCounter = 0; { // Validate range overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; @@ -52,7 +52,7 @@ struct instantiator { assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); assert(equal(expected, span{input}.first<3>())); - assert(comparisonsCounter == ranges::size(input)); + assert(projectionCounter == ranges::size(input)); } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp index 90b837e05cf..e5228540ed6 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp @@ -33,10 +33,10 @@ struct instantiator { if constexpr (non_proxy || !is_permissive) { using ranges::remove_copy, ranges::remove_copy_result, ranges::equal, ranges::iterator_t; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; + size_t projectionCounter = 0; + auto projection = [&projectionCounter](const P& val) { + ++projectionCounter; + return val.second; }; { // Validate iterator + sentinel overload @@ -48,10 +48,10 @@ struct instantiator { assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 3); assert(equal(output, expected)); - assert(comparisonsCounter == ranges::size(input)); + assert(projectionCounter == ranges::size(input)); } - comparisonsCounter = 0; + projectionCounter = 0; { // Validate range overload P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; @@ -62,7 +62,7 @@ struct instantiator { assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 3); assert(equal(output, expected)); - assert(comparisonsCounter == ranges::size(input)); + assert(projectionCounter == ranges::size(input)); } } } diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp index c38be85b257..23453dccd76 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp @@ -35,10 +35,10 @@ struct instantiator { if constexpr (non_proxy || !is_permissive) { using ranges::remove_copy_if, ranges::remove_copy_if_result, ranges::equal, ranges::iterator_t; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; + size_t projectionCounter = 0; + auto projection = [&projectionCounter](const P& val) { + ++projectionCounter; + return val.second; }; { // Validate iterator + sentinel overload @@ -50,10 +50,10 @@ struct instantiator { assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 3); assert(equal(output, expected)); - assert(comparisonsCounter == ranges::size(input)); + assert(projectionCounter == ranges::size(input)); } - comparisonsCounter = 0; + projectionCounter = 0; { // Validate range overload P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; @@ -63,7 +63,7 @@ struct instantiator { assert(result.in == wrapped_input.end()); assert(result.out.peek() == output + 3); assert(equal(output, expected)); - assert(comparisonsCounter == ranges::size(input)); + assert(projectionCounter == ranges::size(input)); } } } diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp index 10e443c9adf..386f93e0dfa 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp @@ -12,7 +12,7 @@ using namespace std; using P = pair; -constexpr auto matches = [](int const val) { return val == 47; }; +constexpr auto matches = [](const int val) { return val == 47; }; // Validate dangling story STATIC_ASSERT(same_as{}, matches)), ranges::dangling>); @@ -25,10 +25,10 @@ struct instantiator { static constexpr void call() { using ranges::remove_if, ranges::subrange, ranges::equal, ranges::iterator_t; - size_t comparisonsCounter = 0; - auto projection = [&comparisonsCounter](const P& data) { - ++comparisonsCounter; - return data.second; + size_t projectionCounter = 0; + auto projection = [&projectionCounter](const P& val) { + ++projectionCounter; + return val.second; }; { // Validate iterator + sentinel overload @@ -40,10 +40,10 @@ struct instantiator { assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); assert(equal(expected, span{input}.first<3>())); - assert(comparisonsCounter == ranges::size(input)); + assert(projectionCounter == ranges::size(input)); } - comparisonsCounter = 0; + projectionCounter = 0; { // Validate range overload P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; @@ -54,7 +54,7 @@ struct instantiator { assert(result.begin() == next(wrapped_input.begin(), 3)); assert(result.end() == wrapped_input.end()); assert(equal(expected, span{input}.first<3>())); - assert(comparisonsCounter == ranges::size(input)); + assert(projectionCounter == ranges::size(input)); } } }; From f4c82bab1c1ce27f41760ca8264af9b19df0fe40 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 13 Jul 2020 10:48:33 -0700 Subject: [PATCH 7/8] Apply suggestions from code review Casey's last-minute comment nitpick. --- stl/inc/algorithm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 308e1271bd5..c1ad93e1168 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -386,8 +386,8 @@ namespace ranges { inline constexpr _For_each_n_fn for_each_n{_Not_quite_object::_Construct_tag{}}; // VARIABLE ranges::find - // concept-constrained for strict enforcement as it is used by several algorithms. // clang-format off + // concept-constrained for strict enforcement as it is used by several algorithms template _Se, class _Ty, class _Pj> requires indirect_binary_predicate, const _Ty*> _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj) { @@ -444,7 +444,7 @@ _NODISCARD _FwdIt find_if(_ExPo&& _Exec, _FwdIt _First, const _FwdIt _Last, _Pr #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::find_if - // concept-constrained for strict enforcement as it is used by several algorithms. + // concept-constrained for strict enforcement as it is used by several algorithms template _Se, class _Pj, indirect_unary_predicate> _Pr> _NODISCARD constexpr _It _Find_if_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { for (; _First != _Last; ++_First) { From ea920069214cdc7dc55532ead9b66310d2d9629b Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 16 Jul 2020 16:51:43 -0700 Subject: [PATCH 8/8] Resolve some more VSO-938163 issues triggered by merging with the copy_backward PR --- .../tests/P0896R4_ranges_alg_remove/test.cpp | 64 ++++++++++--------- .../P0896R4_ranges_alg_remove_if/test.cpp | 58 +++++++++-------- 2 files changed, 67 insertions(+), 55 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp index 769c64d6ab4..3ddb3c33703 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp @@ -21,38 +21,44 @@ struct instantiator { template static constexpr void call() { - using ranges::remove, ranges::subrange, ranges::equal, ranges::iterator_t; - - size_t projectionCounter = 0; - auto projection = [&projectionCounter](const P& val) { - ++projectionCounter; - return val.second; - }; - - { // Validate iterator + sentinel overload - P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; - Read wrapped_input{input}; - - auto result = remove(wrapped_input.begin(), wrapped_input.end(), 47, projection); - STATIC_ASSERT(same_as>>); - assert(result.begin() == next(wrapped_input.begin(), 3)); - assert(result.end() == wrapped_input.end()); - assert(equal(expected, span{input}.first<3>())); - assert(projectionCounter == ranges::size(input)); - } +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 +#pragma warning(suppress : 4127) // conditional expression is constant + if (!ranges::contiguous_range || !is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using ranges::remove, ranges::subrange, ranges::equal, ranges::iterator_t; + + size_t projectionCounter = 0; + auto projection = [&projectionCounter](const P& val) { + ++projectionCounter; + return val.second; + }; + + { // Validate iterator + sentinel overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; + + auto result = remove(wrapped_input.begin(), wrapped_input.end(), 47, projection); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, span{input}.first<3>())); + assert(projectionCounter == ranges::size(input)); + } - projectionCounter = 0; + projectionCounter = 0; - { // Validate range overload - P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; - Read wrapped_input{input}; + { // Validate range overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; - auto result = remove(wrapped_input, 47, projection); - STATIC_ASSERT(same_as>>); - assert(result.begin() == next(wrapped_input.begin(), 3)); - assert(result.end() == wrapped_input.end()); - assert(equal(expected, span{input}.first<3>())); - assert(projectionCounter == ranges::size(input)); + auto result = remove(wrapped_input, 47, projection); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, span{input}.first<3>())); + assert(projectionCounter == ranges::size(input)); + } } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp index 386f93e0dfa..863a0e28917 100644 --- a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp @@ -23,38 +23,44 @@ struct instantiator { template static constexpr void call() { - using ranges::remove_if, ranges::subrange, ranges::equal, ranges::iterator_t; +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 +#pragma warning(suppress : 4127) // conditional expression is constant + if (!ranges::contiguous_range || !is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using ranges::remove_if, ranges::subrange, ranges::equal, ranges::iterator_t; - size_t projectionCounter = 0; - auto projection = [&projectionCounter](const P& val) { - ++projectionCounter; - return val.second; - }; + size_t projectionCounter = 0; + auto projection = [&projectionCounter](const P& val) { + ++projectionCounter; + return val.second; + }; - { // Validate iterator + sentinel overload - P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; - Read wrapped_input{input}; + { // Validate iterator + sentinel overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; - auto result = remove_if(wrapped_input.begin(), wrapped_input.end(), matches, projection); - STATIC_ASSERT(same_as>>); - assert(result.begin() == next(wrapped_input.begin(), 3)); - assert(result.end() == wrapped_input.end()); - assert(equal(expected, span{input}.first<3>())); - assert(projectionCounter == ranges::size(input)); - } + auto result = remove_if(wrapped_input.begin(), wrapped_input.end(), matches, projection); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, span{input}.first<3>())); + assert(projectionCounter == ranges::size(input)); + } - projectionCounter = 0; + projectionCounter = 0; - { // Validate range overload - P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; - Read wrapped_input{input}; + { // Validate range overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; - auto result = remove_if(wrapped_input, matches, projection); - STATIC_ASSERT(same_as>>); - assert(result.begin() == next(wrapped_input.begin(), 3)); - assert(result.end() == wrapped_input.end()); - assert(equal(expected, span{input}.first<3>())); - assert(projectionCounter == ranges::size(input)); + auto result = remove_if(wrapped_input, matches, projection); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, span{input}.first<3>())); + assert(projectionCounter == ranges::size(input)); + } } } };