From 45f296d4cc636e314acca0f4f959d6db0dc1f96d Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 13 Oct 2020 14:48:09 +0200 Subject: [PATCH 01/14] Implement drop_while_view --- stl/inc/algorithm | 52 --- stl/inc/ranges | 98 +++++ stl/inc/xutility | 48 +++ tests/std/test.lst | 2 + .../tests/P0896R4_views_drop_while/env.lst | 4 + .../tests/P0896R4_views_drop_while/test.cpp | 401 ++++++++++++++++++ .../P0896R4_views_drop_while_death/env.lst | 4 + .../P0896R4_views_drop_while_death/test.cpp | 39 ++ 8 files changed, 596 insertions(+), 52 deletions(-) create mode 100644 tests/std/tests/P0896R4_views_drop_while/env.lst create mode 100644 tests/std/tests/P0896R4_views_drop_while/test.cpp create mode 100644 tests/std/tests/P0896R4_views_drop_while_death/env.lst create mode 100644 tests/std/tests/P0896R4_views_drop_while_death/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 9e0d2458f7d..fdea68f832b 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -499,58 +499,6 @@ template _Se, class _Pj = identity, - indirect_unary_predicate> _Pr> - _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { - _Adl_verify_range(_First, _Last); - - auto _UResult = _Find_if_not_unchecked( - _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); - - _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 { - auto _First = _RANGES begin(_Range); - - auto _UResult = _Find_if_not_unchecked( - _Get_unwrapped(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); - - _Seek_wrapped(_First, _STD move(_UResult)); - return _First; - } - - private: - template - _NODISCARD static constexpr _It _Find_if_not_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { - _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); - - for (; _First != _Last; ++_First) { - if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - break; - } - } - - return _First; - } - }; - - inline constexpr _Find_if_not_fn find_if_not{_Not_quite_object::_Construct_tag{}}; -} // namespace ranges -#endif // __cpp_lib_concepts - // FUNCTION TEMPLATE adjacent_find template _NODISCARD _CONSTEXPR20 _FwdIt adjacent_find(const _FwdIt _First, _FwdIt _Last, _Pr _Pred) { diff --git a/stl/inc/ranges b/stl/inc/ranges index 776e1310efe..b855b50f3e5 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1915,6 +1915,104 @@ namespace ranges { inline constexpr _Drop_fn drop; } // namespace views + // CLASS TEMPLATE ranges::drop_while_view + // clang-format off + template + requires input_range<_Vw> && is_object_v<_Pr> && indirect_unary_predicate> + class drop_while_view : public _Cached_position_t, _Vw, drop_while_view<_Vw, _Pr>> { + // clang-format on + private: + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ _Semiregular_box<_Pr> _Pred{}; + + public: + drop_while_view() = default; + + constexpr drop_while_view(_Vw _Range_, _Pr _Pred_) noexcept( + is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)), _Pred{in_place, _STD move(_Pred_)} {} + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr const _Pr& pred() const noexcept /* strengthened */ { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Pred, "value-initialized drop_while_view has no predicate"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return *_Pred; + } + + _NODISCARD constexpr auto begin() { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Pred, "N4861 [range.drop.while.view] forbids calling begin on a drop_while_view that holds no " + "predicate"); // Per LWG-XXXX +#endif // _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (forward_range<_Vw>) { + if (this->_Has_cache()) { + return this->_Get_cache(_Range); + } + } + + auto _First = _RANGES find_if_not(_Range, _STD cref(*_Pred)); + if constexpr (forward_range<_Vw>) { + this->_Set_cache(_Range, _First); + } + + return _STD move(_First); + } + + _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES end(_Range))) { + return _RANGES end(_Range); + } + }; + + template + drop_while_view(_Rng&&, _Pr) -> drop_while_view, _Pr>; + + namespace views { + // VARIABLE views::drop_while + class _Drop_while_fn { + private: + template + struct _Partial : _Pipe::_Base<_Partial<_Pr>> { + /* [[no_unique_address]] */ _Semiregular_box<_Pr> _Pred; + + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept( + noexcept(drop_while_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)})) requires requires { + drop_while_view{static_cast<_Rng&&>(_Range), _STD move(*_Pred)}; + } + { + // clang-format on + return drop_while_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)}; + } + }; + + public: + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pr _Pred) const + noexcept(noexcept(drop_while_view{_STD forward<_Rng>(_Range), _STD move(_Pred)})) requires requires { + drop_while_view{static_cast<_Rng&&>(_Range), _STD move(_Pred)}; + } + { + // clang-format on + return drop_while_view{_STD forward<_Rng>(_Range), _STD move(_Pred)}; + } + + template <_Copy_constructible_object _Pr> + _NODISCARD constexpr auto operator()(_Pr _Pred) const noexcept(is_nothrow_move_constructible_v<_Pr>) { + return _Partial<_Pr>{._Pred = {in_place, _STD move(_Pred)}}; + } + }; + + inline constexpr _Drop_while_fn drop_while; + } // namespace views + // CLASS TEMPLATE ranges::reverse_view // clang-format off template diff --git a/stl/inc/xutility b/stl/inc/xutility index 7dbdc5aa5b6..a1c5c9a627b 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5849,6 +5849,54 @@ namespace ranges { }; inline constexpr _Find_if_fn find_if{_Not_quite_object::_Construct_tag{}}; + + // VARIABLE ranges::find_if_not + class _Find_if_not_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + + auto _UResult = _Find_if_not_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _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 { + auto _First = _RANGES begin(_Range); + + auto _UResult = _Find_if_not_unchecked( + _Get_unwrapped(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + private: + template + _NODISCARD static constexpr _It _Find_if_not_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + for (; _First != _Last; ++_First) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + break; + } + } + + return _First; + } + }; + + inline constexpr _Find_if_not_fn find_if_not{_Not_quite_object::_Construct_tag{}}; } // namespace ranges #endif // __cpp_lib_concepts diff --git a/tests/std/test.lst b/tests/std/test.lst index ce4aff5bb8d..2d7ad09a685 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -340,6 +340,8 @@ tests\P0896R4_ranges_to_address tests\P0896R4_stream_iterators tests\P0896R4_views_all tests\P0896R4_views_drop +tests\P0896R4_views_drop_while +tests\P0896R4_views_drop_while_death tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death diff --git a/tests/std/tests/P0896R4_views_drop_while/env.lst b/tests/std/tests/P0896R4_views_drop_while/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_drop_while/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_drop_while/test.cpp b/tests/std/tests/P0896R4_views_drop_while/test.cpp new file mode 100644 index 00000000000..3bb70749d19 --- /dev/null +++ b/tests/std/tests/P0896R4_views_drop_while/test.cpp @@ -0,0 +1,401 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +// Test a silly precomposed range adaptor pipeline +constexpr auto is_less_than_three = [](const auto& x) { return x < 3; }; +using Pred = remove_const_t; +STATIC_ASSERT(is_nothrow_copy_constructible_v&& is_nothrow_move_constructible_v); + +constexpr auto pipeline = views::drop_while(is_less_than_three) | views::drop_while(is_less_than_three) + | views::drop_while(is_less_than_three) | views::drop_while(is_less_than_three); + +template > +using pipeline_t = ranges::drop_while_view< + ranges::drop_while_view, Pred>, Pred>, Pred>; + +template +concept CanViewdrop_while = requires(Rng&& r) { + views::drop_while(static_cast(r), is_less_than_three); +}; + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::drop_while_view, ranges::bidirectional_range, ranges::common_range, ranges::contiguous_range, + ranges::enable_borrowed_range, ranges::forward_range, ranges::iterator_t, ranges::prev, + ranges::random_access_range; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using R = drop_while_view; + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(ranges::input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(contiguous_range == contiguous_range); + + // Validate range adaptor object and range adaptor closure + constexpr auto drop_while_even = views::drop_while(is_less_than_three); + + // ... with lvalue argument + STATIC_ASSERT(CanViewdrop_while == (!is_view || copyable) ); + if constexpr (CanViewdrop_while) { // Validate lvalue + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop_while(rng, is_less_than_three)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | drop_while_even) == is_noexcept); + + STATIC_ASSERT(same_as>); + STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewdrop_while&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop_while(as_const(rng), is_less_than_three)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | drop_while_even) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RC = drop_while_view&>, Pred>; + constexpr bool is_noexcept = + is_nothrow_constructible_v&, decltype((is_less_than_three))>; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop_while(as_const(rng), is_less_than_three)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | drop_while_even) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewdrop_while> == is_view || enable_borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v; + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop_while(move(rng), is_less_than_three)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | drop_while_even) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } else if constexpr (enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = drop_while_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop_while(move(rng), is_less_than_three)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | drop_while_even) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewdrop_while> == (is_view && copyable) + || (!is_view && enable_borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop_while(as_const(rng), is_less_than_three)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | drop_while_even) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } else if constexpr (!is_view && enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = drop_while_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop_while(move(as_const(rng)), is_less_than_three)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_while_even) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } + + // Validate deduction guide +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + same_as auto r = drop_while_view{forward(rng), is_less_than_three}; + assert(ranges::equal(r, expected)); + if constexpr (forward_range) { + // drop_while_view memoizes the first iterator, let's repeat a few times for coverage. + assert(ranges::equal(r, expected)); + assert(ranges::equal(r, expected)); + assert(ranges::equal(r, expected)); + } + + { // Validate drop_while_view::pred + [[maybe_unused]] same_as auto pred_copy = as_const(r).pred(); + STATIC_ASSERT(noexcept(as_const(r).pred())); + } + + const bool is_empty = ranges::empty(expected); + + + // Validate view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty == forward_range); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(r) == is_empty); + assert(static_cast(r) == !is_empty); + } + } + + STATIC_ASSERT(!CanMemberEmpty); + STATIC_ASSERT(!CanBool); + + // Validate drop_while_view::begin + STATIC_ASSERT(CanMemberBegin); + if (forward_range) { // intentionally not if constexpr + // Ditto "let's make some extra calls because memoization" + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + assert(*r.begin() == *begin(expected)); + assert(*r.begin() == *begin(expected)); + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + assert(*r2.begin() == *i2); + assert(*r2.begin() == *i2); + if (!is_empty) { + assert(*i2 == *i); + } + } + + STATIC_ASSERT(!CanBegin); + } + + // Validate drop_while_view::end + STATIC_ASSERT(CanMemberEnd); + if (!is_empty) { + if constexpr (common_range) { + same_as> auto i = r.end(); + if constexpr (bidirectional_range) { + assert(*prev(i) == *prev(end(expected))); + } + } else { + [[maybe_unused]] same_as> auto s = r.end(); + } + + if constexpr (bidirectional_range && common_range && copyable) { + auto r2 = r; + assert(*prev(r2.end()) == *prev(end(expected))); + } + + STATIC_ASSERT(!CanEnd); + } + + // Validate view_interface::data + STATIC_ASSERT(CanMemberData == contiguous_range); + STATIC_ASSERT(CanData == contiguous_range); + if constexpr (contiguous_range) { + const same_as>*> auto ptr1 = r.data(); + assert(to_address(ptr1) == to_address(r.begin())); + } + STATIC_ASSERT(!CanData); + + // Validate view_interface::size + STATIC_ASSERT(CanMemberSize == CanSize); + if constexpr (CanMemberSize) { + assert(r.size() == static_cast(ranges::size(expected))); + } else { + STATIC_ASSERT(!CanSize); + } + + // Validate view_interface::operator[] + STATIC_ASSERT(CanIndex == random_access_range); + STATIC_ASSERT(!CanIndex); + + // Validate view_interface::front and back + STATIC_ASSERT(CanMemberFront == forward_range); + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + STATIC_ASSERT(!CanMemberFront); + STATIC_ASSERT(!CanMemberBack); + + if (!is_empty) { + if constexpr (forward_range) { + assert(r.front() == *begin(expected)); + } + + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + } + + // Validate drop_while_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (copy_constructible && forward_range) { + same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + assert(*b1.begin() == 0); // NB: depends on the test data + if constexpr (bidirectional_range && common_range) { + assert(*prev(b1.end()) == *prev(end(expected))); // NB: depends on the test data + } + } + } + + // Validate drop_while_view::base() && (NB: do this last since it leaves r moved-from) +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + if (forward_range) { // intentionally not if constexpr + same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == 0); // NB: depends on the test data + if constexpr (bidirectional_range && common_range) { + assert(*prev(b2.end()) == *prev(end(expected))); // NB: depends on the test data + } + } + } + return true; +} + +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; +static constexpr int only_larger_than_two[] = {3, 4, 5, 6, 7}; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one(r, only_larger_than_two); + } +}; + +template +using test_range = test::range}, IsCommon, + test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_in(); +#else // ^^^ test all input range permutations / test only "interesting" permutations vvv + // The view is sensitive to category and commonality, but oblivious to size, differencing, and proxyness. + using test::Common; + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING +} + +template > +using move_only_view = test::range}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +int main() { + // Validate views + { // ... copyable + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_larger_than_two)); + test_one(s, only_larger_than_two); + } + { // ... move-only + test_one(move_only_view{some_ints}, only_larger_than_two); + test_one(move_only_view{some_ints}, only_larger_than_two); + test_one(move_only_view{some_ints}, only_larger_than_two); + test_one(move_only_view{some_ints}, only_larger_than_two); + test_one(move_only_view{some_ints}, only_larger_than_two); + test_one(move_only_view{some_ints}, only_larger_than_two); + test_one(move_only_view{some_ints}, only_larger_than_two); + } + + // Validate non-views + { + STATIC_ASSERT(test_one(some_ints, only_larger_than_two)); + test_one(some_ints, only_larger_than_two); + } + { + vector vec(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(vec, only_larger_than_two); + } + { + forward_list lst(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(lst, only_larger_than_two); + } + + // Validate a non-view borrowed range + { + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_larger_than_two)); + test_one(s, only_larger_than_two); + } + + // drop_while/reverse interaction test + { + auto dwr_pipe = views::drop_while(is_less_than_three) | views::reverse; + auto rdw_pipe = views::reverse | views::drop_while(is_less_than_three); + + auto r0 = some_ints | dwr_pipe; + using R0 = decltype(r0); + STATIC_ASSERT(ranges::bidirectional_range && ranges::view); + assert(ranges::equal(r0, views::reverse(only_larger_than_two))); + + auto r1 = some_ints | rdw_pipe; + using R1 = decltype(r1); + STATIC_ASSERT(ranges::bidirectional_range && ranges::view); + assert(ranges::equal(r1, views::reverse(some_ints))); + + assert(!ranges::equal(r0, r1)); + } + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); +} diff --git a/tests/std/tests/P0896R4_views_drop_while_death/env.lst b/tests/std/tests/P0896R4_views_drop_while_death/env.lst new file mode 100644 index 00000000000..22f1f0230a4 --- /dev/null +++ b/tests/std/tests/P0896R4_views_drop_while_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_drop_while_death/test.cpp b/tests/std/tests/P0896R4_views_drop_while_death/test.cpp new file mode 100644 index 00000000000..a81b520657f --- /dev/null +++ b/tests/std/tests/P0896R4_views_drop_while_death/test.cpp @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include +#include + +#include +using namespace std; + +static int some_ints[] = {0, 1, 2, 3}; + +[[maybe_unused]] constexpr auto lambda = [x = 42](int) { return x == 42; }; +using DWV = decltype(ranges::drop_while_view{some_ints, lambda}); + +void test_view_predicate() { + DWV r; + (void) r.pred(); // value-initialized drop_while_view has no predicate +} + +void test_view_begin() { + DWV r; + (void) + r.begin(); // N4861 [range.drop_while.view]/3 forbids calling begin on a drop_while_view that holds no predicate +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + + exec.add_death_tests({ + test_view_predicate, + test_view_begin, + }); + + return exec.run(argc, argv); +} From c173b159016c64af13a6cc679785c129f2837039 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 13 Oct 2020 16:24:26 +0200 Subject: [PATCH 02/14] No unused variables were left behind --- tests/std/tests/P0896R4_views_drop_while_death/test.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P0896R4_views_drop_while_death/test.cpp b/tests/std/tests/P0896R4_views_drop_while_death/test.cpp index a81b520657f..678fab3fc1e 100644 --- a/tests/std/tests/P0896R4_views_drop_while_death/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while_death/test.cpp @@ -6,15 +6,13 @@ #include #include #include -#include +#include #include using namespace std; -static int some_ints[] = {0, 1, 2, 3}; - [[maybe_unused]] constexpr auto lambda = [x = 42](int) { return x == 42; }; -using DWV = decltype(ranges::drop_while_view{some_ints, lambda}); +using DWV = decltype(ranges::drop_while_view{span{}, lambda}); void test_view_predicate() { DWV r; From a4599dbefa1a86608251ce6e817befbb0a0de237 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 13 Oct 2020 22:12:25 +0200 Subject: [PATCH 03/14] Improve tests to also verify that tail remains --- tests/std/tests/P0896R4_views_drop_while/test.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P0896R4_views_drop_while/test.cpp b/tests/std/tests/P0896R4_views_drop_while/test.cpp index 3bb70749d19..814110e06f3 100644 --- a/tests/std/tests/P0896R4_views_drop_while/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while/test.cpp @@ -298,8 +298,9 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { return true; } -static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; -static constexpr int only_larger_than_two[] = {3, 4, 5, 6, 7}; +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 3, 2, 1}; +static constexpr int only_larger_than_two[] = {3, 4, 3, 2, 1}; +static constexpr int only_larger_than_two_reverse[] = {0, 1, 2, 3, 4, 3}; struct instantiator { template @@ -391,9 +392,7 @@ int main() { auto r1 = some_ints | rdw_pipe; using R1 = decltype(r1); STATIC_ASSERT(ranges::bidirectional_range && ranges::view); - assert(ranges::equal(r1, views::reverse(some_ints))); - - assert(!ranges::equal(r0, r1)); + assert(ranges::equal(r1, views::reverse(only_larger_than_two_reverse))); } STATIC_ASSERT((instantiation_test(), true)); From 3e590df5386035a36efa50b84094f9bcedfe76fc Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 13 Oct 2020 22:18:33 +0200 Subject: [PATCH 04/14] Fix typos --- tests/std/tests/P0896R4_views_drop_while/test.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/std/tests/P0896R4_views_drop_while/test.cpp b/tests/std/tests/P0896R4_views_drop_while/test.cpp index 814110e06f3..de106129dc2 100644 --- a/tests/std/tests/P0896R4_views_drop_while/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while/test.cpp @@ -26,7 +26,7 @@ using pipeline_t = ranges::drop_while_view< ranges::drop_while_view, Pred>, Pred>, Pred>; template -concept CanViewdrop_while = requires(Rng&& r) { +concept CanViewDrop_while = requires(Rng&& r) { views::drop_while(static_cast(r), is_less_than_three); }; @@ -51,8 +51,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { constexpr auto drop_while_even = views::drop_while(is_less_than_three); // ... with lvalue argument - STATIC_ASSERT(CanViewdrop_while == (!is_view || copyable) ); - if constexpr (CanViewdrop_while) { // Validate lvalue + STATIC_ASSERT(CanViewDrop_while == (!is_view || copyable) ); + if constexpr (CanViewDrop_while) { // Validate lvalue constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; STATIC_ASSERT(same_as); @@ -66,7 +66,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { } // ... with const lvalue argument - STATIC_ASSERT(CanViewdrop_while&> == (!is_view || copyable) ); + STATIC_ASSERT(CanViewDrop_while&> == (!is_view || copyable) ); if constexpr (is_view && copyable) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v; @@ -94,7 +94,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { } // ... with rvalue argument - STATIC_ASSERT(CanViewdrop_while> == is_view || enable_borrowed_range>); + STATIC_ASSERT(CanViewDrop_while> == is_view || enable_borrowed_range>); if constexpr (is_view) { constexpr bool is_noexcept = is_nothrow_move_constructible_v; STATIC_ASSERT(same_as); @@ -121,7 +121,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { } // ... with const rvalue argument - STATIC_ASSERT(CanViewdrop_while> == (is_view && copyable) + STATIC_ASSERT(CanViewDrop_while> == (is_view && copyable) || (!is_view && enable_borrowed_range>) ); if constexpr (is_view && copyable) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v; @@ -169,7 +169,6 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { const bool is_empty = ranges::empty(expected); - // Validate view_interface::empty and operator bool STATIC_ASSERT(CanMemberEmpty == forward_range); STATIC_ASSERT(CanBool == CanEmpty); From 6626d2cf355530323ca7518959adc809ec1e6f41 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 23 Oct 2020 09:46:10 +0200 Subject: [PATCH 05/14] Commit Caseys fix Thanks to STLs eagle eyes Co-authored-by: Casey Carter --- tests/std/tests/P0896R4_views_drop_while/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0896R4_views_drop_while/test.cpp b/tests/std/tests/P0896R4_views_drop_while/test.cpp index de106129dc2..79ac3c08b2f 100644 --- a/tests/std/tests/P0896R4_views_drop_while/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while/test.cpp @@ -127,10 +127,10 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v; STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(views::drop_while(as_const(rng), is_less_than_three)) == is_noexcept); + STATIC_ASSERT(noexcept(views::drop_while(move(as_const(rng)), is_less_than_three)) == is_noexcept); STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(as_const(rng) | drop_while_even) == is_noexcept); + STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_while_even) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); From 0227d5db8d4487651356825d5b85d9f2cfbff0bc Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 23 Oct 2020 11:59:30 +0200 Subject: [PATCH 06/14] Remove declval constructs --- tests/std/tests/P0896R4_views_drop_while/test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P0896R4_views_drop_while/test.cpp b/tests/std/tests/P0896R4_views_drop_while/test.cpp index 79ac3c08b2f..8c703861515 100644 --- a/tests/std/tests/P0896R4_views_drop_while/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while/test.cpp @@ -106,9 +106,9 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); } else if constexpr (enable_borrowed_range>) { - using S = decltype(ranges::subrange{declval>()}); + using S = decltype(ranges::subrange{move(rng)}); using RS = drop_while_view; - constexpr bool is_noexcept = noexcept(S{declval>()}); + constexpr bool is_noexcept = noexcept(S{move(rng)}); STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::drop_while(move(rng), is_less_than_three)) == is_noexcept); @@ -135,9 +135,9 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); } else if constexpr (!is_view && enable_borrowed_range>) { - using S = decltype(ranges::subrange{declval>()}); + using S = decltype(ranges::subrange{move(as_const(rng))}); using RS = drop_while_view; - constexpr bool is_noexcept = noexcept(S{declval>()}); + constexpr bool is_noexcept = noexcept(S{move(as_const(rng))}); STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::drop_while(move(as_const(rng)), is_less_than_three)) == is_noexcept); From 9ab7514dd2fdca34b3ee87e758b89e6617c92519 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 29 Oct 2020 16:35:25 +0100 Subject: [PATCH 07/14] Address review comments from Casey --- stl/inc/ranges | 18 +- .../tests/P0896R4_views_drop_while/env.lst | 2 +- .../tests/P0896R4_views_drop_while/test.cpp | 162 +++++++++++------- 3 files changed, 113 insertions(+), 69 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index b855b50f3e5..7fee546f262 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1929,7 +1929,7 @@ namespace ranges { drop_while_view() = default; constexpr drop_while_view(_Vw _Range_, _Pr _Pred_) noexcept( - is_nothrow_move_constructible_v<_Vw>) // strengthened + is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Pr>) // strengthened : _Range(_STD move(_Range_)), _Pred{in_place, _STD move(_Pred_)} {} _NODISCARD constexpr _Vw base() const& noexcept( @@ -1950,7 +1950,7 @@ namespace ranges { _NODISCARD constexpr auto begin() { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Pred, "N4861 [range.drop.while.view] forbids calling begin on a drop_while_view that holds no " - "predicate"); // Per LWG-XXXX + "predicate"); #endif // _CONTAINER_DEBUG_LEVEL > 0 if constexpr (forward_range<_Vw>) { if (this->_Has_cache()) { @@ -1963,7 +1963,7 @@ namespace ranges { this->_Set_cache(_Range, _First); } - return _STD move(_First); + return _First; } _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES end(_Range))) { @@ -1983,7 +1983,17 @@ namespace ranges { /* [[no_unique_address]] */ _Semiregular_box<_Pr> _Pred; template - _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept( + _NODISCARD constexpr auto operator()(_Rng&& _Range) const& noexcept( + noexcept(drop_while_view{_STD forward<_Rng>(_Range), *_Pred})) requires requires { + drop_while_view{static_cast<_Rng&&>(_Range), *_Pred}; + } + { + // clang-format on + return drop_while_view{_STD forward<_Rng>(_Range), *_Pred}; + } + + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) && noexcept( noexcept(drop_while_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)})) requires requires { drop_while_view{static_cast<_Rng&&>(_Range), _STD move(*_Pred)}; } diff --git a/tests/std/tests/P0896R4_views_drop_while/env.lst b/tests/std/tests/P0896R4_views_drop_while/env.lst index 62a24024479..f3ccc8613c6 100644 --- a/tests/std/tests/P0896R4_views_drop_while/env.lst +++ b/tests/std/tests/P0896R4_views_drop_while/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\strict_concepts_matrix.lst +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_drop_while/test.cpp b/tests/std/tests/P0896R4_views_drop_while/test.cpp index 8c703861515..e719e0a5ea7 100644 --- a/tests/std/tests/P0896R4_views_drop_while/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while/test.cpp @@ -14,12 +14,14 @@ using namespace std; // Test a silly precomposed range adaptor pipeline -constexpr auto is_less_than_three = [](const auto& x) { return x < 3; }; -using Pred = remove_const_t; +template +constexpr auto is_less_than = [](const auto& x) { return x < X; }; + +using Pred = remove_const_t)>; STATIC_ASSERT(is_nothrow_copy_constructible_v&& is_nothrow_move_constructible_v); -constexpr auto pipeline = views::drop_while(is_less_than_three) | views::drop_while(is_less_than_three) - | views::drop_while(is_less_than_three) | views::drop_while(is_less_than_three); +constexpr auto pipeline = views::drop_while(is_less_than<0>) | views::drop_while(is_less_than<1>) + | views::drop_while(is_less_than<2>) | views::drop_while(is_less_than<3>); template > using pipeline_t = ranges::drop_while_view< @@ -27,7 +29,7 @@ using pipeline_t = ranges::drop_while_view< template concept CanViewDrop_while = requires(Rng&& r) { - views::drop_while(static_cast(r), is_less_than_three); + views::drop_while(static_cast(r), is_less_than<3>); }; template @@ -48,15 +50,15 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(contiguous_range == contiguous_range); // Validate range adaptor object and range adaptor closure - constexpr auto drop_while_even = views::drop_while(is_less_than_three); + constexpr auto drop_while_even = views::drop_while(is_less_than<3>); // ... with lvalue argument STATIC_ASSERT(CanViewDrop_while == (!is_view || copyable) ); if constexpr (CanViewDrop_while) { // Validate lvalue constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(views::drop_while(rng, is_less_than_three)) == is_noexcept); + STATIC_ASSERT(same_as)), R>); + STATIC_ASSERT(noexcept(views::drop_while(rng, is_less_than<3>)) == is_noexcept); STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(rng | drop_while_even) == is_noexcept); @@ -70,8 +72,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { if constexpr (is_view && copyable) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v; - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(views::drop_while(as_const(rng), is_less_than_three)) == is_noexcept); + STATIC_ASSERT(same_as)), R>); + STATIC_ASSERT(noexcept(views::drop_while(as_const(rng), is_less_than<3>)) == is_noexcept); STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(as_const(rng) | drop_while_even) == is_noexcept); @@ -81,10 +83,10 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { } else if constexpr (!is_view) { using RC = drop_while_view&>, Pred>; constexpr bool is_noexcept = - is_nothrow_constructible_v&, decltype((is_less_than_three))>; + is_nothrow_constructible_v&, decltype((is_less_than<3>) )>; - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(views::drop_while(as_const(rng), is_less_than_three)) == is_noexcept); + STATIC_ASSERT(same_as)), RC>); + STATIC_ASSERT(noexcept(views::drop_while(as_const(rng), is_less_than<3>)) == is_noexcept); STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(as_const(rng) | drop_while_even) == is_noexcept); @@ -97,8 +99,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(CanViewDrop_while> == is_view || enable_borrowed_range>); if constexpr (is_view) { constexpr bool is_noexcept = is_nothrow_move_constructible_v; - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(views::drop_while(move(rng), is_less_than_three)) == is_noexcept); + STATIC_ASSERT(same_as)), R>); + STATIC_ASSERT(noexcept(views::drop_while(move(rng), is_less_than<3>)) == is_noexcept); STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(move(rng) | drop_while_even) == is_noexcept); @@ -110,8 +112,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { using RS = drop_while_view; constexpr bool is_noexcept = noexcept(S{move(rng)}); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(views::drop_while(move(rng), is_less_than_three)) == is_noexcept); + STATIC_ASSERT(same_as)), RS>); + STATIC_ASSERT(noexcept(views::drop_while(move(rng), is_less_than<3>)) == is_noexcept); STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(move(rng) | drop_while_even) == is_noexcept); @@ -126,8 +128,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { if constexpr (is_view && copyable) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v; - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(views::drop_while(move(as_const(rng)), is_less_than_three)) == is_noexcept); + STATIC_ASSERT(same_as)), R>); + STATIC_ASSERT(noexcept(views::drop_while(move(as_const(rng)), is_less_than<3>)) == is_noexcept); STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_while_even) == is_noexcept); @@ -139,8 +141,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { using RS = drop_while_view; constexpr bool is_noexcept = noexcept(S{move(as_const(rng))}); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(views::drop_while(move(as_const(rng)), is_less_than_three)) == is_noexcept); + STATIC_ASSERT(same_as)), RS>); + STATIC_ASSERT(noexcept(views::drop_while(move(as_const(rng)), is_less_than<3>)) == is_noexcept); STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_while_even) == is_noexcept); @@ -153,7 +155,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { #if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 (void) 42; #endif // TRANSITION, DevCom-1159442 - same_as auto r = drop_while_view{forward(rng), is_less_than_three}; + same_as auto r = drop_while_view{forward(rng), is_less_than<3>}; assert(ranges::equal(r, expected)); if constexpr (forward_range) { // drop_while_view memoizes the first iterator, let's repeat a few times for coverage. @@ -183,7 +185,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { } } - STATIC_ASSERT(!CanMemberEmpty); + STATIC_ASSERT(!CanEmpty); STATIC_ASSERT(!CanBool); // Validate drop_while_view::begin @@ -193,17 +195,17 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { const same_as> auto i = r.begin(); if (!is_empty) { assert(*i == *begin(expected)); + assert(*r.begin() == *begin(expected)); + assert(*r.begin() == *begin(expected)); } - assert(*r.begin() == *begin(expected)); - assert(*r.begin() == *begin(expected)); if constexpr (copyable) { auto r2 = r; const same_as> auto i2 = r2.begin(); - assert(*r2.begin() == *i2); - assert(*r2.begin() == *i2); if (!is_empty) { assert(*i2 == *i); + assert(*r2.begin() == *i2); + assert(*r2.begin() == *i2); } } @@ -235,7 +237,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(CanData == contiguous_range); if constexpr (contiguous_range) { const same_as>*> auto ptr1 = r.data(); - assert(to_address(ptr1) == to_address(r.begin())); + assert(ptr1 == to_address(r.begin())); } STATIC_ASSERT(!CanData); @@ -249,23 +251,29 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // Validate view_interface::operator[] STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + if (!is_empty) { + assert(r[0]] == expected[0]); + } + } STATIC_ASSERT(!CanIndex); // Validate view_interface::front and back STATIC_ASSERT(CanMemberFront == forward_range); - STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); - STATIC_ASSERT(!CanMemberFront); - STATIC_ASSERT(!CanMemberBack); - - if (!is_empty) { - if constexpr (forward_range) { + if constexpr (CanMemberFront) { + if (!is_empty) { assert(r.front() == *begin(expected)); } + } + STATIC_ASSERT(!CanMemberFront); - if constexpr (CanMemberBack) { + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + if (!is_empty) { assert(r.back() == *prev(end(expected))); } } + STATIC_ASSERT(!CanMemberBack); // Validate drop_while_view::base() const& STATIC_ASSERT(CanMemberBase == copy_constructible); @@ -275,37 +283,33 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { if (!is_empty) { assert(*b1.begin() == 0); // NB: depends on the test data if constexpr (bidirectional_range && common_range) { - assert(*prev(b1.end()) == *prev(end(expected))); // NB: depends on the test data + assert(*prev(b1.end()) == *prev(end(expected))); } } } - // Validate drop_while_view::base() && (NB: do this last since it leaves r moved-from) -#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 - (void) 42; -#endif // TRANSITION, DevCom-1159442 if (forward_range) { // intentionally not if constexpr same_as auto b2 = move(r).base(); STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); if (!is_empty) { assert(*b2.begin() == 0); // NB: depends on the test data if constexpr (bidirectional_range && common_range) { - assert(*prev(b2.end()) == *prev(end(expected))); // NB: depends on the test data + assert(*prev(b2.end()) == *prev(end(expected))); } } } return true; } -static constexpr int some_ints[] = {0, 1, 2, 3, 4, 3, 2, 1}; -static constexpr int only_larger_than_two[] = {3, 4, 3, 2, 1}; -static constexpr int only_larger_than_two_reverse[] = {0, 1, 2, 3, 4, 3}; +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 3, 2, 1}; +static constexpr int expected[] = {3, 4, 3, 2, 1}; +static constexpr int expected_reverse[] = {0, 1, 2, 3, 4, 3}; struct instantiator { template static constexpr void call() { R r{some_ints}; - test_one(r, only_larger_than_two); + test_one(r, expected); } }; @@ -344,56 +348,86 @@ int main() { // Validate views { // ... copyable constexpr span s{some_ints}; - STATIC_ASSERT(test_one(s, only_larger_than_two)); - test_one(s, only_larger_than_two); + STATIC_ASSERT(test_one(s, expected)); + test_one(s, expected); } { // ... move-only - test_one(move_only_view{some_ints}, only_larger_than_two); - test_one(move_only_view{some_ints}, only_larger_than_two); - test_one(move_only_view{some_ints}, only_larger_than_two); - test_one(move_only_view{some_ints}, only_larger_than_two); - test_one(move_only_view{some_ints}, only_larger_than_two); - test_one(move_only_view{some_ints}, only_larger_than_two); - test_one(move_only_view{some_ints}, only_larger_than_two); + test_one(move_only_view{some_ints}, expected); + test_one(move_only_view{some_ints}, expected); + test_one(move_only_view{some_ints}, expected); + test_one(move_only_view{some_ints}, expected); + test_one(move_only_view{some_ints}, expected); + test_one(move_only_view{some_ints}, expected); + test_one(move_only_view{some_ints}, expected); } // Validate non-views { - STATIC_ASSERT(test_one(some_ints, only_larger_than_two)); - test_one(some_ints, only_larger_than_two); + STATIC_ASSERT(test_one(some_ints, expected)); + test_one(some_ints, expected); } { vector vec(ranges::begin(some_ints), ranges::end(some_ints)); - test_one(vec, only_larger_than_two); + test_one(vec, expected); } { forward_list lst(ranges::begin(some_ints), ranges::end(some_ints)); - test_one(lst, only_larger_than_two); + test_one(lst, expected); } // Validate a non-view borrowed range { constexpr span s{some_ints}; - STATIC_ASSERT(test_one(s, only_larger_than_two)); - test_one(s, only_larger_than_two); + STATIC_ASSERT(test_one(s, expected)); + test_one(s, expected); } // drop_while/reverse interaction test { - auto dwr_pipe = views::drop_while(is_less_than_three) | views::reverse; - auto rdw_pipe = views::reverse | views::drop_while(is_less_than_three); + auto dwr_pipe = views::drop_while(is_less_than<3>) | views::reverse; + auto rdw_pipe = views::reverse | views::drop_while(is_less_than<3>); auto r0 = some_ints | dwr_pipe; using R0 = decltype(r0); STATIC_ASSERT(ranges::bidirectional_range && ranges::view); - assert(ranges::equal(r0, views::reverse(only_larger_than_two))); + assert(ranges::equal(r0, views::reverse(expected))); auto r1 = some_ints | rdw_pipe; using R1 = decltype(r1); STATIC_ASSERT(ranges::bidirectional_range && ranges::view); - assert(ranges::equal(r1, views::reverse(only_larger_than_two_reverse))); + assert(ranges::equal(r1, views::reverse(expected_reverse))); + } + + { // empty range + constexpr span empty{}; + constexpr span empty_expected{}; + STATIC_ASSERT(test_one(empty, empty_expected)); + test_one(empty, empty_expected); } STATIC_ASSERT((instantiation_test(), true)); instantiation_test(); + + { // Validate **non-standard guarantee** that predicates are moved into the range adaptor closure, and into the view + // object from an rvalue closure + struct Fn { + Fn() = default; + Fn(Fn&&) = default; + Fn(const Fn&) { + assert(false); + } + Fn& operator=(Fn&&) = default; + + Fn& operator=(const Fn&) { + assert(false); + return *this; + } + + bool operator()(int) const { + return true; + } + }; + + (void) views::drop_while(Fn{})(span{}); + } } From 9ea6beafc957dafe17503c66090280615a1aeb6a Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 29 Oct 2020 21:07:52 +0100 Subject: [PATCH 08/14] Add reference to lwg-issue --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 7fee546f262..ef817fd328e 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1950,7 +1950,7 @@ namespace ranges { _NODISCARD constexpr auto begin() { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Pred, "N4861 [range.drop.while.view] forbids calling begin on a drop_while_view that holds no " - "predicate"); + "predicate"); // Per LWG-3490 #endif // _CONTAINER_DEBUG_LEVEL > 0 if constexpr (forward_range<_Vw>) { if (this->_Has_cache()) { From 4f7c6a39b23ec63d008bd25fda0741142fe22dfc Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 29 Oct 2020 21:08:58 +0100 Subject: [PATCH 09/14] Fix typo --- tests/std/tests/P0896R4_views_drop_while/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0896R4_views_drop_while/test.cpp b/tests/std/tests/P0896R4_views_drop_while/test.cpp index e719e0a5ea7..21a1740cc81 100644 --- a/tests/std/tests/P0896R4_views_drop_while/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while/test.cpp @@ -253,7 +253,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(CanIndex == random_access_range); if constexpr (CanIndex) { if (!is_empty) { - assert(r[0]] == expected[0]); + assert(r[0] == expected[0]); } } STATIC_ASSERT(!CanIndex); From 74ff137e4793b95eec662dc1b7166bcf6f451141 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 29 Oct 2020 21:25:28 +0100 Subject: [PATCH 10/14] Fix broken test --- tests/std/tests/P0896R4_views_drop_while/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0896R4_views_drop_while/test.cpp b/tests/std/tests/P0896R4_views_drop_while/test.cpp index 21a1740cc81..9560c23de48 100644 --- a/tests/std/tests/P0896R4_views_drop_while/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while/test.cpp @@ -20,8 +20,8 @@ constexpr auto is_less_than = [](const auto& x) { return x < X; }; using Pred = remove_const_t)>; STATIC_ASSERT(is_nothrow_copy_constructible_v&& is_nothrow_move_constructible_v); -constexpr auto pipeline = views::drop_while(is_less_than<0>) | views::drop_while(is_less_than<1>) - | views::drop_while(is_less_than<2>) | views::drop_while(is_less_than<3>); +constexpr auto pipeline = views::drop_while(is_less_than<3>) | views::drop_while(is_less_than<3>) + | views::drop_while(is_less_than<3>) | views::drop_while(is_less_than<3>); template > using pipeline_t = ranges::drop_while_view< From 909f63b34921c4bde9680f9297644d565686b031 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 29 Oct 2020 22:20:13 +0100 Subject: [PATCH 11/14] Naming is hard --- .../tests/P0896R4_views_drop_while/test.cpp | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/tests/std/tests/P0896R4_views_drop_while/test.cpp b/tests/std/tests/P0896R4_views_drop_while/test.cpp index 9560c23de48..c52b67f1634 100644 --- a/tests/std/tests/P0896R4_views_drop_while/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while/test.cpp @@ -301,15 +301,15 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { return true; } -static constexpr int some_ints[] = {0, 1, 2, 3, 4, 3, 2, 1}; -static constexpr int expected[] = {3, 4, 3, 2, 1}; -static constexpr int expected_reverse[] = {0, 1, 2, 3, 4, 3}; +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 3, 2, 1}; +static constexpr int expected_output[] = {3, 4, 3, 2, 1}; +static constexpr int expected_output_reverse[] = {0, 1, 2, 3, 4, 3}; struct instantiator { template static constexpr void call() { R r{some_ints}; - test_one(r, expected); + test_one(r, expected_output); } }; @@ -348,38 +348,38 @@ int main() { // Validate views { // ... copyable constexpr span s{some_ints}; - STATIC_ASSERT(test_one(s, expected)); - test_one(s, expected); + STATIC_ASSERT(test_one(s, expected_output)); + test_one(s, expected_output); } { // ... move-only - test_one(move_only_view{some_ints}, expected); - test_one(move_only_view{some_ints}, expected); - test_one(move_only_view{some_ints}, expected); - test_one(move_only_view{some_ints}, expected); - test_one(move_only_view{some_ints}, expected); - test_one(move_only_view{some_ints}, expected); - test_one(move_only_view{some_ints}, expected); + test_one(move_only_view{some_ints}, expected_output); + test_one(move_only_view{some_ints}, expected_output); + test_one(move_only_view{some_ints}, expected_output); + test_one(move_only_view{some_ints}, expected_output); + test_one(move_only_view{some_ints}, expected_output); + test_one(move_only_view{some_ints}, expected_output); + test_one(move_only_view{some_ints}, expected_output); } // Validate non-views { - STATIC_ASSERT(test_one(some_ints, expected)); - test_one(some_ints, expected); + STATIC_ASSERT(test_one(some_ints, expected_output)); + test_one(some_ints, expected_output); } { vector vec(ranges::begin(some_ints), ranges::end(some_ints)); - test_one(vec, expected); + test_one(vec, expected_output); } { forward_list lst(ranges::begin(some_ints), ranges::end(some_ints)); - test_one(lst, expected); + test_one(lst, expected_output); } // Validate a non-view borrowed range { constexpr span s{some_ints}; - STATIC_ASSERT(test_one(s, expected)); - test_one(s, expected); + STATIC_ASSERT(test_one(s, expected_output)); + test_one(s, expected_output); } // drop_while/reverse interaction test @@ -390,19 +390,17 @@ int main() { auto r0 = some_ints | dwr_pipe; using R0 = decltype(r0); STATIC_ASSERT(ranges::bidirectional_range && ranges::view); - assert(ranges::equal(r0, views::reverse(expected))); + assert(ranges::equal(r0, views::reverse(expected_output))); auto r1 = some_ints | rdw_pipe; using R1 = decltype(r1); STATIC_ASSERT(ranges::bidirectional_range && ranges::view); - assert(ranges::equal(r1, views::reverse(expected_reverse))); + assert(ranges::equal(r1, views::reverse(expected_output_reverse))); } { // empty range - constexpr span empty{}; - constexpr span empty_expected{}; - STATIC_ASSERT(test_one(empty, empty_expected)); - test_one(empty, empty_expected); + STATIC_ASSERT(test_one(span{}, span{})); + test_one(span{}, span{}); } STATIC_ASSERT((instantiation_test(), true)); From c967db54fef1de333fc21edf5bc623f73b6f14e9 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 29 Oct 2020 18:53:41 -0700 Subject: [PATCH 12/14] Tweak error message --- stl/inc/ranges | 3 +-- tests/std/tests/P0896R4_views_drop_while_death/test.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index ef817fd328e..debd84ef4c9 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1949,8 +1949,7 @@ namespace ranges { _NODISCARD constexpr auto begin() { #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Pred, "N4861 [range.drop.while.view] forbids calling begin on a drop_while_view that holds no " - "predicate"); // Per LWG-3490 + _STL_VERIFY(_Pred, "LWG-3490 forbids calling begin on a drop_while_view with no predicate"); #endif // _CONTAINER_DEBUG_LEVEL > 0 if constexpr (forward_range<_Vw>) { if (this->_Has_cache()) { diff --git a/tests/std/tests/P0896R4_views_drop_while_death/test.cpp b/tests/std/tests/P0896R4_views_drop_while_death/test.cpp index 678fab3fc1e..feb5c926892 100644 --- a/tests/std/tests/P0896R4_views_drop_while_death/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while_death/test.cpp @@ -21,8 +21,7 @@ void test_view_predicate() { void test_view_begin() { DWV r; - (void) - r.begin(); // N4861 [range.drop_while.view]/3 forbids calling begin on a drop_while_view that holds no predicate + (void) r.begin(); // LWG-3490 forbids calling begin on a drop_while_view with no predicate } int main(int argc, char* argv[]) { From ac0102f83408db704d26787e28409f2f888bbf68 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 3 Nov 2020 19:52:34 -0800 Subject: [PATCH 13/14] STL's review comments --- stl/inc/ranges | 17 ++------ .../P0896R4_ranges_range_machinery/test.cpp | 1 + .../tests/P0896R4_views_drop_while/test.cpp | 42 +++++++++---------- 3 files changed, 26 insertions(+), 34 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index debd84ef4c9..aa04fc45c7f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1965,7 +1965,7 @@ namespace ranges { return _First; } - _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES end(_Range))) { + _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES end(_Range))) /* strengthened*/ { return _RANGES end(_Range); } }; @@ -1986,20 +1986,14 @@ namespace ranges { noexcept(drop_while_view{_STD forward<_Rng>(_Range), *_Pred})) requires requires { drop_while_view{static_cast<_Rng&&>(_Range), *_Pred}; } - { - // clang-format on - return drop_while_view{_STD forward<_Rng>(_Range), *_Pred}; - } + { return drop_while_view{_STD forward<_Rng>(_Range), *_Pred}; } template _NODISCARD constexpr auto operator()(_Rng&& _Range) && noexcept( noexcept(drop_while_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)})) requires requires { drop_while_view{static_cast<_Rng&&>(_Range), _STD move(*_Pred)}; } - { - // clang-format on - return drop_while_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)}; - } + { return drop_while_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)}; } }; public: @@ -2008,10 +2002,7 @@ namespace ranges { noexcept(noexcept(drop_while_view{_STD forward<_Rng>(_Range), _STD move(_Pred)})) requires requires { drop_while_view{static_cast<_Rng&&>(_Range), _STD move(_Pred)}; } - { - // clang-format on - return drop_while_view{_STD forward<_Rng>(_Range), _STD move(_Pred)}; - } + { return drop_while_view{_STD forward<_Rng>(_Range), _STD move(_Pred)}; } template <_Copy_constructible_object _Pr> _NODISCARD constexpr auto operator()(_Pr _Pred) const noexcept(is_nothrow_move_constructible_v<_Pr>) { diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 1605cb5ebbe..662c3caffaa 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -91,6 +91,7 @@ STATIC_ASSERT(test_cpo(ranges::cdata)); STATIC_ASSERT(test_cpo(ranges::views::all)); STATIC_ASSERT(test_cpo(ranges::views::drop)); +STATIC_ASSERT(test_cpo(ranges::views::drop_while)); STATIC_ASSERT(test_cpo(ranges::views::filter)); STATIC_ASSERT(test_cpo(ranges::views::reverse)); STATIC_ASSERT(test_cpo(ranges::views::single)); diff --git a/tests/std/tests/P0896R4_views_drop_while/test.cpp b/tests/std/tests/P0896R4_views_drop_while/test.cpp index c52b67f1634..ab834b012a7 100644 --- a/tests/std/tests/P0896R4_views_drop_while/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while/test.cpp @@ -28,7 +28,7 @@ using pipeline_t = ranges::drop_while_view< ranges::drop_while_view, Pred>, Pred>, Pred>; template -concept CanViewDrop_while = requires(Rng&& r) { +concept CanViewDropWhile = requires(Rng&& r) { views::drop_while(static_cast(r), is_less_than<3>); }; @@ -50,33 +50,33 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(contiguous_range == contiguous_range); // Validate range adaptor object and range adaptor closure - constexpr auto drop_while_even = views::drop_while(is_less_than<3>); + constexpr auto closure = views::drop_while(is_less_than<3>); // ... with lvalue argument - STATIC_ASSERT(CanViewDrop_while == (!is_view || copyable) ); - if constexpr (CanViewDrop_while) { // Validate lvalue + STATIC_ASSERT(CanViewDropWhile == (!is_view || copyable) ); + if constexpr (CanViewDropWhile) { // Validate lvalue constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; STATIC_ASSERT(same_as)), R>); STATIC_ASSERT(noexcept(views::drop_while(rng, is_less_than<3>)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(rng | drop_while_even) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | closure) == is_noexcept); STATIC_ASSERT(same_as>); STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); } // ... with const lvalue argument - STATIC_ASSERT(CanViewDrop_while&> == (!is_view || copyable) ); + STATIC_ASSERT(CanViewDropWhile&> == (!is_view || copyable) ); if constexpr (is_view && copyable) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v; STATIC_ASSERT(same_as)), R>); STATIC_ASSERT(noexcept(views::drop_while(as_const(rng), is_less_than<3>)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(as_const(rng) | drop_while_even) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); @@ -88,22 +88,22 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as)), RC>); STATIC_ASSERT(noexcept(views::drop_while(as_const(rng), is_less_than<3>)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(as_const(rng) | drop_while_even) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); } // ... with rvalue argument - STATIC_ASSERT(CanViewDrop_while> == is_view || enable_borrowed_range>); + STATIC_ASSERT(CanViewDropWhile> == is_view || enable_borrowed_range>); if constexpr (is_view) { constexpr bool is_noexcept = is_nothrow_move_constructible_v; STATIC_ASSERT(same_as)), R>); STATIC_ASSERT(noexcept(views::drop_while(move(rng), is_less_than<3>)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(rng) | drop_while_even) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -115,15 +115,15 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as)), RS>); STATIC_ASSERT(noexcept(views::drop_while(move(rng), is_less_than<3>)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(rng) | drop_while_even) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); } // ... with const rvalue argument - STATIC_ASSERT(CanViewDrop_while> == (is_view && copyable) + STATIC_ASSERT(CanViewDropWhile> == (is_view && copyable) || (!is_view && enable_borrowed_range>) ); if constexpr (is_view && copyable) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v; @@ -131,8 +131,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as)), R>); STATIC_ASSERT(noexcept(views::drop_while(move(as_const(rng)), is_less_than<3>)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_while_even) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -144,8 +144,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as)), RS>); STATIC_ASSERT(noexcept(views::drop_while(move(as_const(rng)), is_less_than<3>)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_while_even) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); From c58dea82f41bdd1de3674f35e9c62dd722dd0acc Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 3 Nov 2020 19:59:04 -0800 Subject: [PATCH 14/14] Fix space in comment --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index aa04fc45c7f..e0e0530b67f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1965,7 +1965,7 @@ namespace ranges { return _First; } - _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES end(_Range))) /* strengthened*/ { + _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES end(_Range))) /* strengthened */ { return _RANGES end(_Range); } };