From 92bf1260c3e0f78701785c75e60953f5ebef0e06 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 31 Oct 2020 22:03:55 +0100 Subject: [PATCH 01/55] Implement ranges::join_view Addresses #39 --- stl/inc/ranges | 346 ++++++++++++++ tests/std/test.lst | 1 + .../P0896R4_ranges_range_machinery/test.cpp | 1 + tests/std/tests/P0896R4_views_join/env.lst | 4 + tests/std/tests/P0896R4_views_join/test.cpp | 422 ++++++++++++++++++ 5 files changed, 774 insertions(+) create mode 100644 tests/std/tests/P0896R4_views_join/env.lst create mode 100644 tests/std/tests/P0896R4_views_join/test.cpp diff --git a/stl/inc/ranges b/stl/inc/ranges index 1720c95babd..59322d8aff4 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2371,6 +2371,352 @@ namespace ranges { inline constexpr _Common_fn common; } // namespace views + // CLASS TEMPLATE ranges::join_view + // clang-format off + template + requires view<_Vw> && input_range> + && (is_reference_v> || view>) + class join_view : public view_interface> { + // clang-format on + private: + using _InnerRng = range_reference_t<_Vw>; + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ views::all_t<_InnerRng> _Inner{}; + + template + class _Sentinel; + + template // TRANSITION, LWG-3289 + struct _Category_base {}; + + // clang-format off + template + requires _Has_member_iterator_category<_InnerTraits> && _Has_member_iterator_category<_OuterTraits> + struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_ref> { + using iterator_category = + conditional_t<_Deref_is_ref + && derived_from + && derived_from, + bidirectional_iterator_tag, + conditional_t<_Deref_is_ref + && derived_from + && derived_from, + forward_iterator_tag, + conditional_t + && derived_from, + input_iterator_tag, + output_iterator_tag>>>; + }; + // clang-format on + + template + class _Iterator : public _Category_base>>, + iterator_traits>>>, + is_reference_v>>> { + private: + template + friend class _Iterator; + template + friend class _Sentinel; + + using _Parent_t = _Maybe_const<_Const, join_view>; + using _Base = _Maybe_const<_Const, _Vw>; + using _SubRng = range_reference_t<_Base>; + using _OuterIter = iterator_t<_Base>; + using _InnerIter = iterator_t<_SubRng>; + + static constexpr bool _Deref_is_ref = is_reference_v<_SubRng>; + + _OuterIter _Outer{}; + _InnerIter _Inner{}; + _Parent_t* _Parent{}; + + constexpr void _Satisfy() { + auto _Update_inner = [this](_SubRng _Range) -> auto& { + if constexpr (_Deref_is_ref) { + return _Range; + } else { + return (_Parent->_Inner = views::all(_STD move(_Range))); + } + }; + + for (; _Outer != _RANGES end(_Parent->_Range); ++_Outer) { + auto& _Tmp = _Update_inner(*_Outer); + _Inner = _RANGES begin(_Tmp); + if (_Inner != _RANGES end(_Tmp)) { + return; + } + } + if constexpr (_Deref_is_ref) { + _Inner = _InnerIter{}; + } + } + +#if _ITERATOR_DEBUG_LEVEL != 0 + constexpr void _Check_dereference() const noexcept { + _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator"); + _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference end join_view iterator"); + _STL_VERIFY(_Inner != _RANGES end(_Parent->_Inner), "cannot dereference end join_view iterator"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + +#if _ITERATOR_DEBUG_LEVEL != 0 + constexpr void _Same_range(const _Iterator& _Right) const noexcept { + _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible join_view iterators"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + public: + using iterator_concept = + conditional_t<_Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng>, + bidirectional_iterator_tag, + conditional_t<_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>, forward_iterator_tag, + input_iterator_tag>>; + using value_type = range_value_t<_SubRng>; + using difference_type = common_type, range_difference_t<_SubRng>>; + + _Iterator() = default; + + constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) + : _Outer{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Adl_verify_range(_Outer, _RANGES end(_Parent_._Range)); + if constexpr (forward_range<_Base>) { + _Adl_verify_range(_RANGES begin(_Parent_._Range), _Outer); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Satisfy(); + } + + // clang-format off + constexpr _Iterator(_Iterator _It) + requires _Const && convertible_to, _OuterIter> + && convertible_to, _InnerIter> + : _Outer{_STD move(_It._Outer)}, _Inner{_STD move(_It._Inner)}, _Parent{_It._Parent} {} + // clang-format on + + _NODISCARD constexpr decltype(auto) operator*() const noexcept(noexcept(*_Inner)) /* strengthened */ { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return *_Inner; + } + + _NODISCARD constexpr _InnerIter operator->() const // Per resolution of LWG-XXXX + noexcept(is_nothrow_copy_constructible_v<_InnerIter>) /* strengthened */ + requires _Has_arrow<_InnerIter>&& copyable<_InnerIter> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Inner; + } + + constexpr _Iterator& operator++() { + if constexpr (_Deref_is_ref) { + if (++_Inner == _RANGES end(*_Outer)) { + ++_Outer; + _Satisfy(); + } + return *this; + } else { + if (++_Inner == _RANGES end(_Parent->_Inner)) { + ++_Outer; + _Satisfy(); + } + return *this; + } + } + + constexpr void operator++(int) { + ++*this; + } + + // clang-format off + constexpr decltype(auto) operator++(int) + requires _Deref_is_ref && forward_range<_Base>&& forward_range<_SubRng> { + // clang-format on + auto _Tmp = *this; + ++*this; + return _Tmp; + } + + // clang-format off + constexpr _Iterator& operator--() + requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng> + && common_range<_SubRng> { + // clang-format on + if (_Outer == _RANGES end(_Parent->_Range)) { + _Inner = _RANGES end(*--_Outer); + } + while (_Inner == _RANGES begin(*_Outer)) { + _Inner = _RANGES end(*--_Outer); + } + --_Inner; + return *this; + } + + // clang-format off + constexpr _Iterator& operator--(int) + requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng> + && common_range<_SubRng> { + // clang-format on + auto _Tmp = *this; + --*this; + return _Tmp; + } + + // clang-format off + _NODISCARD friend constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner)) /* strengthened */ + requires _Deref_is_ref && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Same_range(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner; + } + + _NODISCARD friend constexpr decltype(auto) iter_move(const _Iterator& _It) noexcept( + noexcept(_RANGES iter_move(_It._Inner))) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _It._Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _RANGES iter_move(_It._Inner); + } + + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(ranges::iter_swap(_Left._Inner, _Right._Inner))) requires indirectly_swappable<_InnerIter> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Check_dereference(); + _Right._Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _RANGES iter_swap(_Left._Inner, _Right._Inner); + } + }; + + template + class _Sentinel { + private: + template + friend class _Iterator; + template + friend class _Sentinel; + + using _Parent_t = _Maybe_const<_Const, join_view>; + using _Base = _Maybe_const<_Const, _Vw>; + + template + using _Maybe_const_iter = iterator_t<_Maybe_const<_OtherConst, _Vw>>; + + sentinel_t<_Base> _Last{}; + + template + _NODISCARD static constexpr const _Maybe_const_iter<_OtherConst>& _Get_current( + const _Iterator<_OtherConst>& _It) noexcept { + return _It._Current; + } + + public: + _Sentinel() = default; + // clang-format off + constexpr explicit _Sentinel(_Parent_t& _Parent) noexcept(noexcept(_RANGES end(_STD declval<_Base>())) + && is_nothrow_move_constructible_v>) // strengthened + : _Last(_RANGES end(_Parent._Range)) {} + + constexpr _Sentinel(_Sentinel _Se) + noexcept(is_nothrow_constructible_v, sentinel_t<_Vw>>) // strengthened + requires _Const && convertible_to, sentinel_t<_Base>> + : _Last(_STD move(_Se._Last)) {} + + template + requires sentinel_for, _Maybe_const_iter<_OtherConst>> + _NODISCARD friend constexpr bool operator==(const _Iterator<_OtherConst>& _Left, + const _Sentinel& _Right) noexcept(noexcept(_Get_current(_Right) == _Last)) /* strengthened */ { + // clang-format on + return _Get_current(_Left) == _Right._Last; + } + }; + + public: + join_view() = default; + constexpr explicit join_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)) {} + + _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 _Iterator begin() noexcept( + noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { + return _Iterator{*this, _RANGES begin(_Range)}; + } + + _NODISCARD constexpr _Iterator begin() noexcept( + noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ + requires _Simple_view<_Vw>&& is_reference_v> { + return _Iterator{*this, _RANGES begin(_Range)}; + } + + // clang-format off + _NODISCARD constexpr _Iterator begin() const noexcept(noexcept( + _RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ + requires input_range && is_reference_v> { + // clang-format on + return _Iterator{*this, _RANGES begin(_Range)}; + } + + // clang-format off + _NODISCARD constexpr auto end() noexcept(noexcept( + _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { + if constexpr (forward_range<_Vw> && is_reference_v> + && forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { + // clang-format on + return _Iterator<_Simple_view<_Vw>>{*this, _RANGES end(_Range)}; + } else { + return _Sentinel<_Simple_view<_Vw>>{*this}; + } + } + + // clang-format off + _NODISCARD constexpr auto end() const noexcept(noexcept( + _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ + requires input_range && is_reference_v> { + if constexpr (forward_range && is_reference_v> + && forward_range> && common_range + && common_range>) { + // clang-format on + return _Iterator{*this, _RANGES end(_Range)}; + } else { + return _Sentinel{*this}; + } + } + }; + + template + join_view(_Rng &&) -> join_view>; + + namespace views { + // VARIABLE views::join + class _Join_fn : public _Pipe::_Base<_Join_fn> { + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept( + join_view>{_STD forward<_Rng>(_Range)})) requires requires { + join_view>{static_cast<_Rng&&>(_Range)}; + } { + // clang-format on + return join_view>{_STD forward<_Rng>(_Range)}; + } + }; + + inline constexpr _Join_fn join; + } // namespace views + // CLASS TEMPLATE ranges::reverse_view // clang-format off template diff --git a/tests/std/test.lst b/tests/std/test.lst index f23814ec915..0aa735006d8 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -369,6 +369,7 @@ tests\P0896R4_views_elements tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death +tests\P0896R4_views_join tests\P0896R4_views_reverse tests\P0896R4_views_single tests\P0896R4_views_take diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 576c4340517..3e9b709b978 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -104,6 +104,7 @@ STATIC_ASSERT(test_cpo(ranges::views::drop)); STATIC_ASSERT(test_cpo(ranges::views::drop_while)); STATIC_ASSERT(test_cpo(ranges::views::elements<42>)); STATIC_ASSERT(test_cpo(ranges::views::filter)); +STATIC_ASSERT(test_cpo(ranges::views::join)); STATIC_ASSERT(test_cpo(ranges::views::keys)); STATIC_ASSERT(test_cpo(ranges::views::reverse)); STATIC_ASSERT(test_cpo(ranges::views::single)); diff --git a/tests/std/tests/P0896R4_views_join/env.lst b/tests/std/tests/P0896R4_views_join/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_views_join/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_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp new file mode 100644 index 00000000000..2f5b51f479f --- /dev/null +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -0,0 +1,422 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +template +concept CanViewJoin = requires(Rng&& r) { + views::join(static_cast(r)); +}; + +// Test a silly precomposed range adaptor pipeline +constexpr auto pipeline = views::all | views::join; + +template +constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { + using ranges::begin, ranges::bidirectional_range, ranges::common_range, ranges::enable_borrowed_range, ranges::end, + ranges::forward_range, ranges::input_range, ranges::iterator_t, ranges::join_view, ranges::range_value_t; + + constexpr bool deref_is_reference = is_reference_v>; + constexpr bool is_view = ranges::view>; + + if constexpr (deref_is_reference || is_view) { + using V = views::all_t; + using R = join_view; + static_assert(ranges::view); + /* + static_assert(input_range == input_range && input_range); + static_assert(forward_range == forward_range && forward_range); + static_assert(bidirectional_range == bidirectional_range && bidirectional_range); + static_assert(!ranges::random_access_range); + static_assert(!ranges::contiguous_range); + + // Validate range adapter object + // ...with lvalue argument + static_assert(CanViewJoin == (!is_view || copyable) ); + if constexpr (CanViewJoin) { + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + static_assert(same_as); + static_assert(noexcept(views::join(rng)) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(rng | views::join) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(rng | pipeline) == is_noexcept); + } + + // ... with const lvalue argument + static_assert(CanViewJoin&> == (!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::join(as_const(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::join) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RC = join_view&>>; + constexpr bool is_noexcept = is_nothrow_constructible_v&>; + + static_assert(same_as); + static_assert(noexcept(views::join(as_const(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::join) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | pipeline) == is_noexcept); + } + + // ... with rvalue argument + static_assert( + CanViewJoin> == 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::join(move(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::join) == 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{move(rng)}); + using RS = join_view; + constexpr bool is_noexcept = noexcept(S{move(rng)}); + + static_assert(same_as); + static_assert(noexcept(views::join(move(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::join) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // ... with const rvalue argument + static_assert(CanViewJoin> == (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::join(move(as_const(rng)))) == is_nothrow_copy_constructible_v); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | views::join) == is_nothrow_copy_constructible_v); + + 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{as_const(rng)}); + using RS = join_view; + constexpr bool is_noexcept = noexcept(S{as_const(rng)}); + + static_assert(same_as); + static_assert(noexcept(views::join(move(as_const(rng)))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | views::join) == 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 = join_view{forward(rng)}; + assert(ranges::equal(r, expected)); + + /* + // Validate join_view::size + static_assert(CanMemberSize == sized_range); + if constexpr (sized_range) { + assert(r.size() == static_cast>(size(expected))); + static_assert(noexcept(r.size()) == noexcept(size(rng))); + } + + static_assert(CanMemberSize == sized_range); + if constexpr (sized_range) { + assert(as_const(r).size() == static_cast>(size(expected))); + static_assert(noexcept(r.size()) == noexcept(size(as_const(rng)))); + } + + // Validate view_interface::empty and operator bool + const bool is_empty = ranges::empty(expected); + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + static_assert(CanMemberEmpty == common_range); + if constexpr (common_range) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + + // Validate join_view::begin + static_assert(CanMemberBegin); + { + // join_view sometimes caches begin, so let's make several extra calls + const same_as>> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + assert(r.begin() == i); + assert(r.begin() == i); + // NB: non-const begin is unconditionally noexcept(false) due to caching + static_assert(!noexcept(r.begin())); + + 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(CanMemberBegin == common_range); + if constexpr (common_range) { + const same_as>> auto ci = as_const(r).begin(); + assert(as_const(r).begin() == ci); + assert(as_const(r).begin() == ci); + if (!is_empty) { + assert(*ci == *i); + } + static_assert(noexcept(as_const(r).begin()) == noexcept(join_iterator{end(as_const(rng))})); + + if constexpr (copyable) { + const auto r2 = r; + const same_as>> auto ci2 = r2.begin(); + assert(r2.begin() == ci2); + assert(r2.begin() == ci2); + if (!is_empty) { + assert(*ci2 == *i); + } + } + } + } + + // Validate join_view::end + static_assert(CanMemberEnd); + if (!is_empty) { + assert(*prev(r.end()) == *prev(end(expected))); + + if constexpr (copyable) { + auto r2 = r; + assert(*prev(r2.end()) == *prev(end(expected))); + } + static_assert(noexcept(r.end()) == noexcept(join_iterator{begin(rng)})); + + static_assert(CanMemberEnd == common_range); + if constexpr (common_range) { + assert(*prev(as_const(r).end()) == *prev(end(expected))); + static_assert(noexcept(as_const(r).end()) == noexcept(join_iterator{begin(as_const(rng))})); + } + } + + // Validate view_interface::data + static_assert(!CanData); + static_assert(!CanData); + + if (!is_empty) { + // Validate view_interface::operator[] + static_assert(CanIndex == random_access_range); + static_assert(CanIndex == (random_access_range && common_range) ); + if constexpr (random_access_range) { + assert(r[0] == *begin(expected)); + + if constexpr (common_range) { + assert(as_const(r)[0] == *begin(expected)); + } + } + + // Validate view_interface::front and back + assert(r.front() == *begin(expected)); + assert(r.back() == *prev(end(expected))); + + static_assert(CanMemberFront == common_range); + static_assert(CanMemberBack == common_range); + if constexpr (common_range) { + assert(as_const(r).front() == *begin(expected)); + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate join_view::base() const& + static_assert(CanMemberBase == copy_constructible); + if constexpr (copy_constructible) { + 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() == *prev(end(expected))); + if constexpr (common_range) { + assert(*prev(b1.end()) == *begin(expected)); + } + } + } + + // Validate join_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 + same_as auto b2 = move(r).base(); + static_assert(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == *prev(end(expected))); + if constexpr (common_range) { + assert(*prev(b2.end()) == *begin(expected)); + } + } + */ + } + return true; +} + +template +struct with_dependent_input_ranges { + template + static constexpr void call() { + using namespace test; + using test::range; + + // For all ranges, IsCommon implies Eq. + // For single-pass ranges, Eq is uninteresting without IsCommon (there's only one valid iterator + // value at a time, and no reason to compare it with itself for equality). + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + with_forward_ranges::template call(); + } +}; + +template +constexpr void test_nested_inout() { + with_input_or_output_ranges, Element>::call(); +} + +static constexpr int full_range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static constexpr span first_range{full_range, full_range + 3}; +static constexpr span second_range{full_range + 3, full_range + 7}; +static constexpr span third_range{full_range + 7, full_range + 7}; // NB: Intentionally left empty +static constexpr span fourth_range{full_range + 7, full_range + 10}; + +struct instantiator { + template + static constexpr void call() { + array inner_ranges = { + Inner{first_range}, Inner{second_range}, Inner{third_range}, Inner{fourth_range}}; + Outer r{inner_ranges}; + test_one(r, first_range, full_range); + } +}; + +template > +using move_only_view = test::range}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +int main() { + // Validate views + { // ...copyable + array data = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + span input{data}; + constexpr string_view expected = "Hello World!"sv; + static_assert(test_one(input, string_view{}, expected)); + test_one(input, string_view{}, expected); + } + /* + { // ... move-only + test_one(move_only_view{some_ints}, joind_ints); + test_one(move_only_view{some_ints}, joind_ints); + test_one(move_only_view{some_ints}, joind_ints); + test_one(move_only_view{some_ints}, joind_ints); + test_one(move_only_view{some_ints}, joind_ints); + test_one(move_only_view{some_ints}, joind_ints); + } + + // Validate non-views + { // ... C array + static_assert(test_one(some_ints, joind_ints)); + test_one(some_ints, joind_ints); + } + { // ... contiguous container + string str{"Hello, World!"}; + constexpr auto expected = "!dlroW ,olleH"sv; + test_one(str, expected); + } + { // ... bidi container + list lst{3, 4, 5}; + static constexpr int joind[] = {5, 4, 3}; + test_one(lst, joind); + + static constexpr int joind_prefix[] = {4, 3}; + assert(ranges::equal( + views::join(ranges::subrange{counted_iterator{lst.begin(), 2}, default_sentinel}), joind_prefix)); + } + + // Validate a non-view borrowed range + { + constexpr span s{some_ints}; + static_assert(test_one(s, joind_ints)); + test_one(s, joind_ints); + } + // Get full instantiation coverage + static_assert((test_nested_inout(), true)); + test_nested_inout(); + */ +} From 6766d6b185d92372f87ed4aca4f27666f257b6ad Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 25 Jan 2021 13:24:06 -0800 Subject: [PATCH 02/55] Reorder: counted & common are after join and before reverse --- stl/inc/ranges | 873 +++++++++++++++++++++++++------------------------ 1 file changed, 437 insertions(+), 436 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 59322d8aff4..1bbf31c533d 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2116,280 +2116,123 @@ namespace ranges { inline constexpr _Drop_fn drop; - // VARIABLE views::counted - class _Counted_fn { - private: - enum class _St { _Span, _Subrange, _Subrange_counted }; - - template - _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { - _STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>); - if constexpr (contiguous_iterator<_It>) { - return {_St::_Span, noexcept(span{_STD to_address(_STD declval<_It>()), iter_difference_t<_It>{}})}; - } else if constexpr (random_access_iterator<_It>) { - return {_St::_Subrange, - noexcept(subrange{_STD declval<_It>(), _STD declval<_It>() + iter_difference_t<_It>{}})}; - } else { - return {_St::_Subrange_counted, - noexcept(subrange{ - counted_iterator{_STD declval<_It>(), iter_difference_t<_It>{}}, default_sentinel})}; - } - } - - template - static constexpr _Choice_t<_St> _Choice = _Choose<_It>(); - - public: - // clang-format off - template - requires input_or_output_iterator> - _NODISCARD constexpr auto operator()(_It&& _First, const iter_difference_t> _Count) const - noexcept(_Choice>._No_throw) { - // clang-format on - _STL_ASSERT(_Count >= 0, "The size passed to views::counted must be non-negative"); - constexpr _St _Strat = _Choice>._Strategy; - - if constexpr (_Strat == _St::_Span) { - return span{_STD to_address(_STD forward<_It>(_First)), static_cast(_Count)}; - } else if constexpr (_Strat == _St::_Subrange) { - return subrange{_First, _First + _Count}; - } else if constexpr (_Strat == _St::_Subrange_counted) { - return subrange{counted_iterator{_STD forward<_It>(_First), _Count}, default_sentinel}; - } - } - }; - - inline constexpr _Counted_fn counted; - } // namespace views - - // CLASS TEMPLATE ranges::drop_while_view - // clang-format off + // 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{}; + // clang-format on + private: + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ _Semiregular_box<_Pr> _Pred{}; - public: - drop_while_view() = default; + public: + drop_while_view() = default; - constexpr drop_while_view(_Vw _Range_, _Pr _Pred_) noexcept( - is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Pr>) // strengthened - : _Range(_STD move(_Range_)), _Pred{in_place, _STD move(_Pred_)} {} + constexpr drop_while_view(_Vw _Range_, _Pr _Pred_) noexcept( + 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( - 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 _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 */ { + _NODISCARD constexpr const _Pr& pred() const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Pred, "value-initialized drop_while_view has no predicate"); + _STL_VERIFY(_Pred, "value-initialized drop_while_view has no predicate"); #endif // _CONTAINER_DEBUG_LEVEL > 0 - return *_Pred; - } + return *_Pred; + } - _NODISCARD constexpr auto begin() { + _NODISCARD constexpr auto begin() { #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Pred, "LWG-3490 forbids calling begin on a drop_while_view with no predicate"); + _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()) { - 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 _First; - } - - _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES end(_Range))) /* strengthened */ { - 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), *_Pred})) requires requires { - drop_while_view{static_cast<_Rng&&>(_Range), *_Pred}; + if constexpr (forward_range<_Vw>) { + if (this->_Has_cache()) { + return this->_Get_cache(_Range); + } } - { 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)}; + auto _First = _RANGES find_if_not(_Range, _STD cref(*_Pred)); + if constexpr (forward_range<_Vw>) { + this->_Set_cache(_Range, _First); } - { 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)}; + return _First; } - { 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)}}; + _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES end(_Range))) /* strengthened */ { + return _RANGES end(_Range); } }; - inline constexpr _Drop_while_fn drop_while; - } // namespace views - - // CLASS TEMPLATE ranges::common_view - // clang-format off - template - requires (!common_range<_Vw> && copyable>) - class common_view : public view_interface> { - // clang-format on - private: - /* [[no_unique_address]] */ _Vw _Base{}; - - public: - common_view() = default; - constexpr explicit common_view(_Vw _Base_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened - : _Base(_STD move(_Base_)) {} - - _NODISCARD constexpr _Vw base() const& noexcept( - is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { - return _Base; - } - _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { - return _STD move(_Base); - } - - _NODISCARD constexpr auto begin() noexcept( - noexcept(_RANGES begin(_Base)) && is_nothrow_move_constructible_v>) /* strengthened */ { - if constexpr (random_access_range<_Vw> && sized_range<_Vw>) { - return _RANGES begin(_Base); - } else { - return common_iterator, sentinel_t<_Vw>>{_RANGES begin(_Base)}; - } - } - - _NODISCARD constexpr auto begin() const noexcept( - noexcept(_RANGES begin(_Base)) - && is_nothrow_move_constructible_v>) /* strengthened */ requires range { - if constexpr (random_access_range && sized_range) { - return _RANGES begin(_Base); - } else { - return common_iterator, sentinel_t>{_RANGES begin(_Base)}; - } - } - - _NODISCARD constexpr auto end() { - if constexpr (random_access_range<_Vw> && sized_range<_Vw>) { - return _RANGES begin(_Base) + _RANGES size(_Base); - } else { - return common_iterator, sentinel_t<_Vw>>{_RANGES end(_Base)}; - } - } - - _NODISCARD constexpr auto end() const requires range { - if constexpr (random_access_range && sized_range) { - return _RANGES begin(_Base) + _RANGES size(_Base); - } else { - return common_iterator, sentinel_t>{_RANGES end(_Base)}; - } - } - - _NODISCARD constexpr auto size() noexcept( - noexcept(_RANGES size(_Base))) /* strengthened */ requires sized_range<_Vw> { - return _RANGES size(_Base); - } - _NODISCARD constexpr auto size() const - noexcept(noexcept(_RANGES size(_Base))) /* strengthened */ requires sized_range { - return _RANGES size(_Base); - } - }; - - template - common_view(_Rng&&) -> common_view>; + 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), *_Pred})) requires requires { + drop_while_view{static_cast<_Rng&&>(_Range), *_Pred}; + } + { return drop_while_view{_STD forward<_Rng>(_Range), *_Pred}; } - namespace views { - // VARIABLE views::common - class _Common_fn : public _Pipe::_Base<_Common_fn> { - private: - enum class _St { _None, _All, _Common }; + 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)}; + } + { return drop_while_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)}; } + }; - template - _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { - if constexpr (common_range<_Rng>) { - return {_St::_All, noexcept(views::all(_STD declval<_Rng>()))}; - } else if constexpr (copyable>) { - return {_St::_Common, noexcept(common_view{_STD declval<_Rng>()})}; - } else { - return {_St::_None}; + 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)}; } - } - - template - static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); - - public: - // clang-format off - template - requires (_Choice<_Rng>._Strategy != _St::_None) - _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(_Choice<_Rng>._No_throw) { - // clang-format on - constexpr _St _Strat = _Choice<_Rng>._Strategy; + { return drop_while_view{_STD forward<_Rng>(_Range), _STD move(_Pred)}; } - if constexpr (_Strat == _St::_All) { - return views::all(_STD forward<_Rng>(_Range)); - } else if constexpr (_Strat == _St::_Common) { - return common_view{_STD forward<_Rng>(_Range)}; - } else { - static_assert(_Always_false<_Rng>, "Should be unreachable"); + 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 _Common_fn common; - } // namespace views + inline constexpr _Drop_while_fn drop_while; + } // namespace views - // CLASS TEMPLATE ranges::join_view - // clang-format off + // CLASS TEMPLATE ranges::join_view + // clang-format off template requires view<_Vw> && input_range> && (is_reference_v> || view>) class join_view : public view_interface> { - // clang-format on - private: - using _InnerRng = range_reference_t<_Vw>; - /* [[no_unique_address]] */ _Vw _Range{}; - /* [[no_unique_address]] */ views::all_t<_InnerRng> _Inner{}; + // clang-format on + private: + using _InnerRng = range_reference_t<_Vw>; + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ views::all_t<_InnerRng> _Inner{}; - template - class _Sentinel; + template + class _Sentinel; - template // TRANSITION, LWG-3289 - struct _Category_base {}; + template // TRANSITION, LWG-3289 + struct _Category_base {}; - // clang-format off + // clang-format off template requires _Has_member_iterator_category<_InnerTraits> && _Has_member_iterator_category<_OuterTraits> struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_ref> { @@ -2407,218 +2250,218 @@ namespace ranges { input_iterator_tag, output_iterator_tag>>>; }; - // clang-format on - - template - class _Iterator : public _Category_base>>, - iterator_traits>>>, - is_reference_v>>> { - private: - template - friend class _Iterator; - template - friend class _Sentinel; - - using _Parent_t = _Maybe_const<_Const, join_view>; - using _Base = _Maybe_const<_Const, _Vw>; - using _SubRng = range_reference_t<_Base>; - using _OuterIter = iterator_t<_Base>; - using _InnerIter = iterator_t<_SubRng>; - - static constexpr bool _Deref_is_ref = is_reference_v<_SubRng>; + // clang-format on - _OuterIter _Outer{}; - _InnerIter _Inner{}; - _Parent_t* _Parent{}; + template + class _Iterator : public _Category_base>>, + iterator_traits>>>, + is_reference_v>>> { + private: + template + friend class _Iterator; + template + friend class _Sentinel; + + using _Parent_t = _Maybe_const<_Const, join_view>; + using _Base = _Maybe_const<_Const, _Vw>; + using _SubRng = range_reference_t<_Base>; + using _OuterIter = iterator_t<_Base>; + using _InnerIter = iterator_t<_SubRng>; + + static constexpr bool _Deref_is_ref = is_reference_v<_SubRng>; + + _OuterIter _Outer{}; + _InnerIter _Inner{}; + _Parent_t* _Parent{}; + + constexpr void _Satisfy() { + auto _Update_inner = [this](_SubRng _Range) -> auto& { + if constexpr (_Deref_is_ref) { + return _Range; + } else { + return (_Parent->_Inner = views::all(_STD move(_Range))); + } + }; - constexpr void _Satisfy() { - auto _Update_inner = [this](_SubRng _Range) -> auto& { - if constexpr (_Deref_is_ref) { - return _Range; - } else { - return (_Parent->_Inner = views::all(_STD move(_Range))); + for (; _Outer != _RANGES end(_Parent->_Range); ++_Outer) { + auto& _Tmp = _Update_inner(*_Outer); + _Inner = _RANGES begin(_Tmp); + if (_Inner != _RANGES end(_Tmp)) { + return; + } } - }; - - for (; _Outer != _RANGES end(_Parent->_Range); ++_Outer) { - auto& _Tmp = _Update_inner(*_Outer); - _Inner = _RANGES begin(_Tmp); - if (_Inner != _RANGES end(_Tmp)) { - return; + if constexpr (_Deref_is_ref) { + _Inner = _InnerIter{}; } } - if constexpr (_Deref_is_ref) { - _Inner = _InnerIter{}; - } - } #if _ITERATOR_DEBUG_LEVEL != 0 - constexpr void _Check_dereference() const noexcept { - _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator"); - _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference end join_view iterator"); - _STL_VERIFY(_Inner != _RANGES end(_Parent->_Inner), "cannot dereference end join_view iterator"); - } + constexpr void _Check_dereference() const noexcept { + _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator"); + _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference end join_view iterator"); + _STL_VERIFY(_Inner != _RANGES end(_Parent->_Inner), "cannot dereference end join_view iterator"); + } #endif // _ITERATOR_DEBUG_LEVEL != 0 #if _ITERATOR_DEBUG_LEVEL != 0 - constexpr void _Same_range(const _Iterator& _Right) const noexcept { - _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible join_view iterators"); - } + constexpr void _Same_range(const _Iterator& _Right) const noexcept { + _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible join_view iterators"); + } #endif // _ITERATOR_DEBUG_LEVEL != 0 - public: - using iterator_concept = - conditional_t<_Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng>, - bidirectional_iterator_tag, - conditional_t<_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>, forward_iterator_tag, - input_iterator_tag>>; - using value_type = range_value_t<_SubRng>; - using difference_type = common_type, range_difference_t<_SubRng>>; + public: + using iterator_concept = + conditional_t<_Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng>, + bidirectional_iterator_tag, + conditional_t<_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>, + forward_iterator_tag, input_iterator_tag>>; + using value_type = range_value_t<_SubRng>; + using difference_type = common_type, range_difference_t<_SubRng>>; - _Iterator() = default; + _Iterator() = default; - constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) - : _Outer{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { + constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) + : _Outer{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { #if _ITERATOR_DEBUG_LEVEL != 0 - _Adl_verify_range(_Outer, _RANGES end(_Parent_._Range)); - if constexpr (forward_range<_Base>) { - _Adl_verify_range(_RANGES begin(_Parent_._Range), _Outer); - } + _Adl_verify_range(_Outer, _RANGES end(_Parent_._Range)); + if constexpr (forward_range<_Base>) { + _Adl_verify_range(_RANGES begin(_Parent_._Range), _Outer); + } #endif // _ITERATOR_DEBUG_LEVEL != 0 - _Satisfy(); - } + _Satisfy(); + } - // clang-format off + // clang-format off constexpr _Iterator(_Iterator _It) requires _Const && convertible_to, _OuterIter> && convertible_to, _InnerIter> : _Outer{_STD move(_It._Outer)}, _Inner{_STD move(_It._Inner)}, _Parent{_It._Parent} {} - // clang-format on + // clang-format on - _NODISCARD constexpr decltype(auto) operator*() const noexcept(noexcept(*_Inner)) /* strengthened */ { + _NODISCARD constexpr decltype(auto) operator*() const noexcept(noexcept(*_Inner)) /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 - _Check_dereference(); + _Check_dereference(); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return *_Inner; - } + return *_Inner; + } - _NODISCARD constexpr _InnerIter operator->() const // Per resolution of LWG-XXXX - noexcept(is_nothrow_copy_constructible_v<_InnerIter>) /* strengthened */ - requires _Has_arrow<_InnerIter>&& copyable<_InnerIter> { + _NODISCARD constexpr _InnerIter operator->() const // Per resolution of LWG-XXXX + noexcept(is_nothrow_copy_constructible_v<_InnerIter>) /* strengthened */ + requires _Has_arrow<_InnerIter>&& copyable<_InnerIter> { #if _ITERATOR_DEBUG_LEVEL != 0 - _Check_dereference(); + _Check_dereference(); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return _Inner; - } + return _Inner; + } - constexpr _Iterator& operator++() { - if constexpr (_Deref_is_ref) { - if (++_Inner == _RANGES end(*_Outer)) { - ++_Outer; - _Satisfy(); - } - return *this; - } else { - if (++_Inner == _RANGES end(_Parent->_Inner)) { - ++_Outer; - _Satisfy(); + constexpr _Iterator& operator++() { + if constexpr (_Deref_is_ref) { + if (++_Inner == _RANGES end(*_Outer)) { + ++_Outer; + _Satisfy(); + } + return *this; + } else { + if (++_Inner == _RANGES end(_Parent->_Inner)) { + ++_Outer; + _Satisfy(); + } + return *this; } - return *this; } - } - constexpr void operator++(int) { - ++*this; - } + constexpr void operator++(int) { + ++*this; + } - // clang-format off + // clang-format off constexpr decltype(auto) operator++(int) requires _Deref_is_ref && forward_range<_Base>&& forward_range<_SubRng> { - // clang-format on - auto _Tmp = *this; - ++*this; - return _Tmp; - } + // clang-format on + auto _Tmp = *this; + ++*this; + return _Tmp; + } - // clang-format off + // clang-format off constexpr _Iterator& operator--() requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng> && common_range<_SubRng> { - // clang-format on - if (_Outer == _RANGES end(_Parent->_Range)) { - _Inner = _RANGES end(*--_Outer); - } - while (_Inner == _RANGES begin(*_Outer)) { - _Inner = _RANGES end(*--_Outer); + // clang-format on + if (_Outer == _RANGES end(_Parent->_Range)) { + _Inner = _RANGES end(*--_Outer); + } + while (_Inner == _RANGES begin(*_Outer)) { + _Inner = _RANGES end(*--_Outer); + } + --_Inner; + return *this; } - --_Inner; - return *this; - } - // clang-format off + // clang-format off constexpr _Iterator& operator--(int) requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng> && common_range<_SubRng> { - // clang-format on - auto _Tmp = *this; - --*this; - return _Tmp; - } + // clang-format on + auto _Tmp = *this; + --*this; + return _Tmp; + } - // clang-format off + // clang-format off _NODISCARD friend constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( noexcept(_Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner)) /* strengthened */ requires _Deref_is_ref && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { - // clang-format on + // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 - _Left._Same_range(_Right); + _Left._Same_range(_Right); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return _Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner; - } + return _Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner; + } - _NODISCARD friend constexpr decltype(auto) iter_move(const _Iterator& _It) noexcept( - noexcept(_RANGES iter_move(_It._Inner))) { + _NODISCARD friend constexpr decltype(auto) iter_move(const _Iterator& _It) noexcept( + noexcept(_RANGES iter_move(_It._Inner))) { #if _ITERATOR_DEBUG_LEVEL != 0 - _It._Check_dereference(); + _It._Check_dereference(); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return _RANGES iter_move(_It._Inner); - } + return _RANGES iter_move(_It._Inner); + } - friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(ranges::iter_swap(_Left._Inner, _Right._Inner))) requires indirectly_swappable<_InnerIter> { + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept(noexcept( + ranges::iter_swap(_Left._Inner, _Right._Inner))) requires indirectly_swappable<_InnerIter> { #if _ITERATOR_DEBUG_LEVEL != 0 - _Left._Check_dereference(); - _Right._Check_dereference(); + _Left._Check_dereference(); + _Right._Check_dereference(); #endif // _ITERATOR_DEBUG_LEVEL != 0 - _RANGES iter_swap(_Left._Inner, _Right._Inner); - } - }; + _RANGES iter_swap(_Left._Inner, _Right._Inner); + } + }; - template - class _Sentinel { - private: - template - friend class _Iterator; - template - friend class _Sentinel; + template + class _Sentinel { + private: + template + friend class _Iterator; + template + friend class _Sentinel; - using _Parent_t = _Maybe_const<_Const, join_view>; - using _Base = _Maybe_const<_Const, _Vw>; + using _Parent_t = _Maybe_const<_Const, join_view>; + using _Base = _Maybe_const<_Const, _Vw>; - template - using _Maybe_const_iter = iterator_t<_Maybe_const<_OtherConst, _Vw>>; + template + using _Maybe_const_iter = iterator_t<_Maybe_const<_OtherConst, _Vw>>; - sentinel_t<_Base> _Last{}; + sentinel_t<_Base> _Last{}; - template - _NODISCARD static constexpr const _Maybe_const_iter<_OtherConst>& _Get_current( - const _Iterator<_OtherConst>& _It) noexcept { - return _It._Current; - } + template + _NODISCARD static constexpr const _Maybe_const_iter<_OtherConst>& _Get_current( + const _Iterator<_OtherConst>& _It) noexcept { + return _It._Current; + } - public: - _Sentinel() = default; - // clang-format off + public: + _Sentinel() = default; + // clang-format off constexpr explicit _Sentinel(_Parent_t& _Parent) noexcept(noexcept(_RANGES end(_STD declval<_Base>())) && is_nothrow_move_constructible_v>) // strengthened : _Last(_RANGES end(_Parent._Range)) {} @@ -2632,89 +2475,247 @@ namespace ranges { requires sentinel_for, _Maybe_const_iter<_OtherConst>> _NODISCARD friend constexpr bool operator==(const _Iterator<_OtherConst>& _Left, const _Sentinel& _Right) noexcept(noexcept(_Get_current(_Right) == _Last)) /* strengthened */ { - // clang-format on - return _Get_current(_Left) == _Right._Last; - } - }; + // clang-format on + return _Get_current(_Left) == _Right._Last; + } + }; - public: - join_view() = default; - constexpr explicit join_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened - : _Range(_STD move(_Range_)) {} + public: + join_view() = default; + constexpr explicit join_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)) {} - _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 _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 _Iterator begin() noexcept( - noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { - return _Iterator{*this, _RANGES begin(_Range)}; - } + _NODISCARD constexpr _Iterator begin() noexcept( + noexcept(_RANGES begin(_Range)) + && is_nothrow_move_constructible_v>) /* strengthened */ { + return _Iterator{*this, _RANGES begin(_Range)}; + } - _NODISCARD constexpr _Iterator begin() noexcept( - noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ - requires _Simple_view<_Vw>&& is_reference_v> { - return _Iterator{*this, _RANGES begin(_Range)}; - } + _NODISCARD constexpr _Iterator begin() noexcept( + noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ + requires _Simple_view<_Vw>&& is_reference_v> { + return _Iterator{*this, _RANGES begin(_Range)}; + } - // clang-format off + // clang-format off _NODISCARD constexpr _Iterator begin() const noexcept(noexcept( _RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ requires input_range && is_reference_v> { - // clang-format on - return _Iterator{*this, _RANGES begin(_Range)}; - } + // clang-format on + return _Iterator{*this, _RANGES begin(_Range)}; + } - // clang-format off + // clang-format off _NODISCARD constexpr auto end() noexcept(noexcept( _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { if constexpr (forward_range<_Vw> && is_reference_v> && forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { - // clang-format on - return _Iterator<_Simple_view<_Vw>>{*this, _RANGES end(_Range)}; - } else { - return _Sentinel<_Simple_view<_Vw>>{*this}; + // clang-format on + return _Iterator<_Simple_view<_Vw>>{*this, _RANGES end(_Range)}; + } else { + return _Sentinel<_Simple_view<_Vw>>{*this}; + } } - } - // clang-format off + // clang-format off _NODISCARD constexpr auto end() const noexcept(noexcept( _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ requires input_range && is_reference_v> { if constexpr (forward_range && is_reference_v> && forward_range> && common_range && common_range>) { + // clang-format on + return _Iterator{*this, _RANGES end(_Range)}; + } else { + return _Sentinel{*this}; + } + } + }; + + template + join_view(_Rng&&) -> join_view>; + + namespace views { + // VARIABLE views::join + class _Join_fn : public _Pipe::_Base<_Join_fn> { + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept( + join_view>{_STD forward<_Rng>(_Range)})) requires requires { + join_view>{static_cast<_Rng&&>(_Range)}; + } { + // clang-format on + return join_view>{_STD forward<_Rng>(_Range)}; + } + }; + + inline constexpr _Join_fn join; + } // namespace views + + // VARIABLE views::counted + class _Counted_fn { + private: + enum class _St { _Span, _Subrange, _Subrange_counted }; + + template + _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { + _STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>); + if constexpr (contiguous_iterator<_It>) { + return {_St::_Span, noexcept(span{_STD to_address(_STD declval<_It>()), iter_difference_t<_It>{}})}; + } else if constexpr (random_access_iterator<_It>) { + return {_St::_Subrange, + noexcept(subrange{_STD declval<_It>(), _STD declval<_It>() + iter_difference_t<_It>{}})}; + } else { + return {_St::_Subrange_counted, + noexcept(subrange{ + counted_iterator{_STD declval<_It>(), iter_difference_t<_It>{}}, default_sentinel})}; + } + } + + template + static constexpr _Choice_t<_St> _Choice = _Choose<_It>(); + + public: + // clang-format off + template + requires input_or_output_iterator> + _NODISCARD constexpr auto operator()(_It&& _First, const iter_difference_t> _Count) const + noexcept(_Choice>._No_throw) { // clang-format on - return _Iterator{*this, _RANGES end(_Range)}; + _STL_ASSERT(_Count >= 0, "The size passed to views::counted must be non-negative"); + constexpr _St _Strat = _Choice>._Strategy; + + if constexpr (_Strat == _St::_Span) { + return span{_STD to_address(_STD forward<_It>(_First)), static_cast(_Count)}; + } else if constexpr (_Strat == _St::_Subrange) { + return subrange{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Subrange_counted) { + return subrange{counted_iterator{_STD forward<_It>(_First), _Count}, default_sentinel}; + } + } + }; + + inline constexpr _Counted_fn counted; + } // namespace views + + // CLASS TEMPLATE ranges::common_view + // clang-format off + template + requires (!common_range<_Vw> && copyable>) + class common_view : public view_interface> { + // clang-format on + private: + /* [[no_unique_address]] */ _Vw _Base{}; + + public: + common_view() = default; + constexpr explicit common_view(_Vw _Base_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Base(_STD move(_Base_)) {} + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Base; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Base); + } + + _NODISCARD constexpr auto begin() noexcept( + noexcept(_RANGES begin(_Base)) && is_nothrow_move_constructible_v>) /* strengthened */ { + if constexpr (random_access_range<_Vw> && sized_range<_Vw>) { + return _RANGES begin(_Base); + } else { + return common_iterator, sentinel_t<_Vw>>{_RANGES begin(_Base)}; + } + } + + _NODISCARD constexpr auto begin() const noexcept( + noexcept(_RANGES begin(_Base)) + && is_nothrow_move_constructible_v>) /* strengthened */ requires range { + if constexpr (random_access_range && sized_range) { + return _RANGES begin(_Base); + } else { + return common_iterator, sentinel_t>{_RANGES begin(_Base)}; + } + } + + _NODISCARD constexpr auto end() { + if constexpr (random_access_range<_Vw> && sized_range<_Vw>) { + return _RANGES begin(_Base) + _RANGES size(_Base); + } else { + return common_iterator, sentinel_t<_Vw>>{_RANGES end(_Base)}; + } + } + + _NODISCARD constexpr auto end() const requires range { + if constexpr (random_access_range && sized_range) { + return _RANGES begin(_Base) + _RANGES size(_Base); } else { - return _Sentinel{*this}; + return common_iterator, sentinel_t>{_RANGES end(_Base)}; } } + + _NODISCARD constexpr auto size() noexcept( + noexcept(_RANGES size(_Base))) /* strengthened */ requires sized_range<_Vw> { + return _RANGES size(_Base); + } + _NODISCARD constexpr auto size() const + noexcept(noexcept(_RANGES size(_Base))) /* strengthened */ requires sized_range { + return _RANGES size(_Base); + } }; template - join_view(_Rng &&) -> join_view>; + common_view(_Rng&&) -> common_view>; namespace views { - // VARIABLE views::join - class _Join_fn : public _Pipe::_Base<_Join_fn> { + // VARIABLE views::common + class _Common_fn : public _Pipe::_Base<_Common_fn> { + private: + enum class _St { _None, _All, _Common }; + + template + _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { + if constexpr (common_range<_Rng>) { + return {_St::_All, noexcept(views::all(_STD declval<_Rng>()))}; + } else if constexpr (copyable>) { + return {_St::_Common, noexcept(common_view{_STD declval<_Rng>()})}; + } else { + return {_St::_None}; + } + } + + template + static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); + public: // clang-format off template - _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept( - join_view>{_STD forward<_Rng>(_Range)})) requires requires { - join_view>{static_cast<_Rng&&>(_Range)}; - } { + requires (_Choice<_Rng>._Strategy != _St::_None) + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(_Choice<_Rng>._No_throw) { // clang-format on - return join_view>{_STD forward<_Rng>(_Range)}; + constexpr _St _Strat = _Choice<_Rng>._Strategy; + + if constexpr (_Strat == _St::_All) { + return views::all(_STD forward<_Rng>(_Range)); + } else if constexpr (_Strat == _St::_Common) { + return common_view{_STD forward<_Rng>(_Range)}; + } else { + static_assert(_Always_false<_Rng>, "Should be unreachable"); + } } }; - inline constexpr _Join_fn join; + inline constexpr _Common_fn common; } // namespace views // CLASS TEMPLATE ranges::reverse_view From f7d45219d9f41e21cb06fa93b15703310f37aaf0 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 25 Jan 2021 15:52:46 -0800 Subject: [PATCH 03/55] Fix indentation; LWG issue is 3500 --- stl/inc/ranges | 608 ++++++++++++++++++++++++------------------------- 1 file changed, 301 insertions(+), 307 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 1bbf31c533d..21e9cff514d 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2115,124 +2115,125 @@ namespace ranges { }; inline constexpr _Drop_fn drop; + } // namespace views - // CLASS TEMPLATE ranges::drop_while_view - // clang-format off + // 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{}; + // clang-format on + private: + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ _Semiregular_box<_Pr> _Pred{}; - public: - drop_while_view() = default; + public: + drop_while_view() = default; - constexpr drop_while_view(_Vw _Range_, _Pr _Pred_) noexcept( - is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Pr>) // strengthened - : _Range(_STD move(_Range_)), _Pred{in_place, _STD move(_Pred_)} {} + constexpr drop_while_view(_Vw _Range_, _Pr _Pred_) noexcept( + 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( - 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 _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 */ { + _NODISCARD constexpr const _Pr& pred() const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Pred, "value-initialized drop_while_view has no predicate"); + _STL_VERIFY(_Pred, "value-initialized drop_while_view has no predicate"); #endif // _CONTAINER_DEBUG_LEVEL > 0 - return *_Pred; - } + return *_Pred; + } - _NODISCARD constexpr auto begin() { + _NODISCARD constexpr auto begin() { #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Pred, "LWG-3490 forbids calling begin on a drop_while_view with no predicate"); + _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()) { - 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); + if constexpr (forward_range<_Vw>) { + if (this->_Has_cache()) { + return this->_Get_cache(_Range); } - - return _First; } - _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES end(_Range))) /* strengthened */ { - return _RANGES end(_Range); + auto _First = _RANGES find_if_not(_Range, _STD cref(*_Pred)); + if constexpr (forward_range<_Vw>) { + this->_Set_cache(_Range, _First); } - }; - 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), *_Pred})) requires requires { - drop_while_view{static_cast<_Rng&&>(_Range), *_Pred}; - } - { return drop_while_view{_STD forward<_Rng>(_Range), *_Pred}; } + return _First; + } - 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)}; - } - { return drop_while_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)}; } - }; + _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES end(_Range))) /* strengthened */ { + return _RANGES end(_Range); + } + }; + + template + drop_while_view(_Rng&&, _Pr) -> drop_while_view, _Pr>; - 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)}; + 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), *_Pred})) requires requires { + drop_while_view{static_cast<_Rng&&>(_Range), *_Pred}; } - { return drop_while_view{_STD forward<_Rng>(_Range), _STD move(_Pred)}; } + { return drop_while_view{_STD forward<_Rng>(_Range), *_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)}}; + 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)}; } + { return drop_while_view{_STD forward<_Rng>(_Range), _STD move(*_Pred)}; } }; - inline constexpr _Drop_while_fn drop_while; - } // namespace views + 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)}; + } + { return drop_while_view{_STD forward<_Rng>(_Range), _STD move(_Pred)}; } - // CLASS TEMPLATE ranges::join_view - // clang-format off + 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::join_view + // clang-format off template requires view<_Vw> && input_range> && (is_reference_v> || view>) class join_view : public view_interface> { - // clang-format on - private: - using _InnerRng = range_reference_t<_Vw>; - /* [[no_unique_address]] */ _Vw _Range{}; - /* [[no_unique_address]] */ views::all_t<_InnerRng> _Inner{}; + // clang-format on + private: + using _InnerRng = range_reference_t<_Vw>; + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ views::all_t<_InnerRng> _Inner{}; - template - class _Sentinel; + template + class _Sentinel; - template // TRANSITION, LWG-3289 - struct _Category_base {}; + template // TRANSITION, LWG-3289 + struct _Category_base {}; - // clang-format off + // clang-format off template requires _Has_member_iterator_category<_InnerTraits> && _Has_member_iterator_category<_OuterTraits> struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_ref> { @@ -2250,218 +2251,218 @@ namespace ranges { input_iterator_tag, output_iterator_tag>>>; }; - // clang-format on + // clang-format on - template - class _Iterator : public _Category_base>>, - iterator_traits>>>, - is_reference_v>>> { - private: - template - friend class _Iterator; - template - friend class _Sentinel; - - using _Parent_t = _Maybe_const<_Const, join_view>; - using _Base = _Maybe_const<_Const, _Vw>; - using _SubRng = range_reference_t<_Base>; - using _OuterIter = iterator_t<_Base>; - using _InnerIter = iterator_t<_SubRng>; - - static constexpr bool _Deref_is_ref = is_reference_v<_SubRng>; - - _OuterIter _Outer{}; - _InnerIter _Inner{}; - _Parent_t* _Parent{}; - - constexpr void _Satisfy() { - auto _Update_inner = [this](_SubRng _Range) -> auto& { - if constexpr (_Deref_is_ref) { - return _Range; - } else { - return (_Parent->_Inner = views::all(_STD move(_Range))); - } - }; + template + class _Iterator : public _Category_base>>, + iterator_traits>>>, + is_reference_v>>> { + private: + template + friend class _Iterator; + template + friend class _Sentinel; - for (; _Outer != _RANGES end(_Parent->_Range); ++_Outer) { - auto& _Tmp = _Update_inner(*_Outer); - _Inner = _RANGES begin(_Tmp); - if (_Inner != _RANGES end(_Tmp)) { - return; - } - } + using _Parent_t = _Maybe_const<_Const, join_view>; + using _Base = _Maybe_const<_Const, _Vw>; + using _SubRng = range_reference_t<_Base>; + using _OuterIter = iterator_t<_Base>; + using _InnerIter = iterator_t<_SubRng>; + + static constexpr bool _Deref_is_ref = is_reference_v<_SubRng>; + + _OuterIter _Outer{}; + _InnerIter _Inner{}; + _Parent_t* _Parent{}; + + constexpr void _Satisfy() { + auto _Update_inner = [this](_SubRng _Range) -> auto& { if constexpr (_Deref_is_ref) { - _Inner = _InnerIter{}; + return _Range; + } else { + return (_Parent->_Inner = views::all(_STD move(_Range))); + } + }; + + for (; _Outer != _RANGES end(_Parent->_Range); ++_Outer) { + auto& _Tmp = _Update_inner(*_Outer); + _Inner = _RANGES begin(_Tmp); + if (_Inner != _RANGES end(_Tmp)) { + return; } } + if constexpr (_Deref_is_ref) { + _Inner = _InnerIter{}; + } + } #if _ITERATOR_DEBUG_LEVEL != 0 - constexpr void _Check_dereference() const noexcept { - _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator"); - _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference end join_view iterator"); - _STL_VERIFY(_Inner != _RANGES end(_Parent->_Inner), "cannot dereference end join_view iterator"); - } + constexpr void _Check_dereference() const noexcept { + _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator"); + _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference end join_view iterator"); + _STL_VERIFY(_Inner != _RANGES end(_Parent->_Inner), "cannot dereference end join_view iterator"); + } #endif // _ITERATOR_DEBUG_LEVEL != 0 #if _ITERATOR_DEBUG_LEVEL != 0 - constexpr void _Same_range(const _Iterator& _Right) const noexcept { - _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible join_view iterators"); - } + constexpr void _Same_range(const _Iterator& _Right) const noexcept { + _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible join_view iterators"); + } #endif // _ITERATOR_DEBUG_LEVEL != 0 - public: - using iterator_concept = - conditional_t<_Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng>, - bidirectional_iterator_tag, - conditional_t<_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>, - forward_iterator_tag, input_iterator_tag>>; - using value_type = range_value_t<_SubRng>; - using difference_type = common_type, range_difference_t<_SubRng>>; + public: + using iterator_concept = + conditional_t<_Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng>, + bidirectional_iterator_tag, + conditional_t<_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>, forward_iterator_tag, + input_iterator_tag>>; + using value_type = range_value_t<_SubRng>; + using difference_type = common_type, range_difference_t<_SubRng>>; - _Iterator() = default; + _Iterator() = default; - constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) - : _Outer{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { + constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) + : _Outer{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { #if _ITERATOR_DEBUG_LEVEL != 0 - _Adl_verify_range(_Outer, _RANGES end(_Parent_._Range)); - if constexpr (forward_range<_Base>) { - _Adl_verify_range(_RANGES begin(_Parent_._Range), _Outer); - } -#endif // _ITERATOR_DEBUG_LEVEL != 0 - _Satisfy(); + _Adl_verify_range(_Outer, _RANGES end(_Parent_._Range)); + if constexpr (forward_range<_Base>) { + _Adl_verify_range(_RANGES begin(_Parent_._Range), _Outer); } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Satisfy(); + } - // clang-format off + // clang-format off constexpr _Iterator(_Iterator _It) requires _Const && convertible_to, _OuterIter> && convertible_to, _InnerIter> : _Outer{_STD move(_It._Outer)}, _Inner{_STD move(_It._Inner)}, _Parent{_It._Parent} {} - // clang-format on + // clang-format on - _NODISCARD constexpr decltype(auto) operator*() const noexcept(noexcept(*_Inner)) /* strengthened */ { + _NODISCARD constexpr decltype(auto) operator*() const noexcept(noexcept(*_Inner)) /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 - _Check_dereference(); + _Check_dereference(); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return *_Inner; - } + return *_Inner; + } - _NODISCARD constexpr _InnerIter operator->() const // Per resolution of LWG-XXXX - noexcept(is_nothrow_copy_constructible_v<_InnerIter>) /* strengthened */ - requires _Has_arrow<_InnerIter>&& copyable<_InnerIter> { + _NODISCARD constexpr _InnerIter operator->() const // Per resolution of LWG-3500 + noexcept(is_nothrow_copy_constructible_v<_InnerIter>) /* strengthened */ + requires _Has_arrow<_InnerIter>&& copyable<_InnerIter> { #if _ITERATOR_DEBUG_LEVEL != 0 - _Check_dereference(); + _Check_dereference(); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return _Inner; - } + return _Inner; + } - constexpr _Iterator& operator++() { - if constexpr (_Deref_is_ref) { - if (++_Inner == _RANGES end(*_Outer)) { - ++_Outer; - _Satisfy(); - } - return *this; - } else { - if (++_Inner == _RANGES end(_Parent->_Inner)) { - ++_Outer; - _Satisfy(); - } - return *this; + constexpr _Iterator& operator++() { + if constexpr (_Deref_is_ref) { + if (++_Inner == _RANGES end(*_Outer)) { + ++_Outer; + _Satisfy(); + } + return *this; + } else { + if (++_Inner == _RANGES end(_Parent->_Inner)) { + ++_Outer; + _Satisfy(); } + return *this; } + } - constexpr void operator++(int) { - ++*this; - } + constexpr void operator++(int) { + ++*this; + } - // clang-format off + // clang-format off constexpr decltype(auto) operator++(int) requires _Deref_is_ref && forward_range<_Base>&& forward_range<_SubRng> { - // clang-format on - auto _Tmp = *this; - ++*this; - return _Tmp; - } + // clang-format on + auto _Tmp = *this; + ++*this; + return _Tmp; + } - // clang-format off + // clang-format off constexpr _Iterator& operator--() requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng> && common_range<_SubRng> { - // clang-format on - if (_Outer == _RANGES end(_Parent->_Range)) { - _Inner = _RANGES end(*--_Outer); - } - while (_Inner == _RANGES begin(*_Outer)) { - _Inner = _RANGES end(*--_Outer); - } - --_Inner; - return *this; + // clang-format on + if (_Outer == _RANGES end(_Parent->_Range)) { + _Inner = _RANGES end(*--_Outer); } + while (_Inner == _RANGES begin(*_Outer)) { + _Inner = _RANGES end(*--_Outer); + } + --_Inner; + return *this; + } - // clang-format off + // clang-format off constexpr _Iterator& operator--(int) requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng> && common_range<_SubRng> { - // clang-format on - auto _Tmp = *this; - --*this; - return _Tmp; - } + // clang-format on + auto _Tmp = *this; + --*this; + return _Tmp; + } - // clang-format off + // clang-format off _NODISCARD friend constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( noexcept(_Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner)) /* strengthened */ requires _Deref_is_ref && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { - // clang-format on + // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 - _Left._Same_range(_Right); + _Left._Same_range(_Right); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return _Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner; - } + return _Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner; + } - _NODISCARD friend constexpr decltype(auto) iter_move(const _Iterator& _It) noexcept( - noexcept(_RANGES iter_move(_It._Inner))) { + _NODISCARD friend constexpr decltype(auto) iter_move(const _Iterator& _It) noexcept( + noexcept(_RANGES iter_move(_It._Inner))) { #if _ITERATOR_DEBUG_LEVEL != 0 - _It._Check_dereference(); + _It._Check_dereference(); #endif // _ITERATOR_DEBUG_LEVEL != 0 - return _RANGES iter_move(_It._Inner); - } + return _RANGES iter_move(_It._Inner); + } - friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept(noexcept( - ranges::iter_swap(_Left._Inner, _Right._Inner))) requires indirectly_swappable<_InnerIter> { + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(ranges::iter_swap(_Left._Inner, _Right._Inner))) requires indirectly_swappable<_InnerIter> { #if _ITERATOR_DEBUG_LEVEL != 0 - _Left._Check_dereference(); - _Right._Check_dereference(); + _Left._Check_dereference(); + _Right._Check_dereference(); #endif // _ITERATOR_DEBUG_LEVEL != 0 - _RANGES iter_swap(_Left._Inner, _Right._Inner); - } - }; + _RANGES iter_swap(_Left._Inner, _Right._Inner); + } + }; - template - class _Sentinel { - private: - template - friend class _Iterator; - template - friend class _Sentinel; + template + class _Sentinel { + private: + template + friend class _Iterator; + template + friend class _Sentinel; - using _Parent_t = _Maybe_const<_Const, join_view>; - using _Base = _Maybe_const<_Const, _Vw>; + using _Parent_t = _Maybe_const<_Const, join_view>; + using _Base = _Maybe_const<_Const, _Vw>; - template - using _Maybe_const_iter = iterator_t<_Maybe_const<_OtherConst, _Vw>>; + template + using _Maybe_const_iter = iterator_t<_Maybe_const<_OtherConst, _Vw>>; - sentinel_t<_Base> _Last{}; + sentinel_t<_Base> _Last{}; - template - _NODISCARD static constexpr const _Maybe_const_iter<_OtherConst>& _Get_current( - const _Iterator<_OtherConst>& _It) noexcept { - return _It._Current; - } + template + _NODISCARD static constexpr const _Maybe_const_iter<_OtherConst>& _Get_current( + const _Iterator<_OtherConst>& _It) noexcept { + return _It._Current; + } - public: - _Sentinel() = default; - // clang-format off + public: + _Sentinel() = default; + // clang-format off constexpr explicit _Sentinel(_Parent_t& _Parent) noexcept(noexcept(_RANGES end(_STD declval<_Base>())) && is_nothrow_move_constructible_v>) // strengthened : _Last(_RANGES end(_Parent._Range)) {} @@ -2475,91 +2476,84 @@ namespace ranges { requires sentinel_for, _Maybe_const_iter<_OtherConst>> _NODISCARD friend constexpr bool operator==(const _Iterator<_OtherConst>& _Left, const _Sentinel& _Right) noexcept(noexcept(_Get_current(_Right) == _Last)) /* strengthened */ { - // clang-format on - return _Get_current(_Left) == _Right._Last; - } - }; + // clang-format on + return _Get_current(_Left) == _Right._Last; + } + }; - public: - join_view() = default; - constexpr explicit join_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened - : _Range(_STD move(_Range_)) {} + public: + join_view() = default; + constexpr explicit join_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)) {} - _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 _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 _Iterator begin() noexcept( - noexcept(_RANGES begin(_Range)) - && is_nothrow_move_constructible_v>) /* strengthened */ { - return _Iterator{*this, _RANGES begin(_Range)}; - } + _NODISCARD constexpr auto begin() noexcept( + noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { + constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v>; + return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + } - _NODISCARD constexpr _Iterator begin() noexcept( - noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ - requires _Simple_view<_Vw>&& is_reference_v> { - return _Iterator{*this, _RANGES begin(_Range)}; - } + // clang-format off + _NODISCARD constexpr _Iterator begin() const noexcept(noexcept( + _RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ + requires input_range && is_reference_v> { + // clang-format on + return _Iterator{*this, _RANGES begin(_Range)}; + } - // clang-format off - _NODISCARD constexpr _Iterator begin() const noexcept(noexcept( - _RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ - requires input_range && is_reference_v> { + // clang-format off + _NODISCARD constexpr auto end() noexcept(noexcept( + _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { + if constexpr (forward_range<_Vw> && is_reference_v> + && forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { // clang-format on - return _Iterator{*this, _RANGES begin(_Range)}; - } - - // clang-format off - _NODISCARD constexpr auto end() noexcept(noexcept( - _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { - if constexpr (forward_range<_Vw> && is_reference_v> - && forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { - // clang-format on - return _Iterator<_Simple_view<_Vw>>{*this, _RANGES end(_Range)}; - } else { - return _Sentinel<_Simple_view<_Vw>>{*this}; - } + return _Iterator<_Simple_view<_Vw>>{*this, _RANGES end(_Range)}; + } else { + return _Sentinel<_Simple_view<_Vw>>{*this}; } + } - // clang-format off - _NODISCARD constexpr auto end() const noexcept(noexcept( - _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ - requires input_range && is_reference_v> { - if constexpr (forward_range && is_reference_v> + // clang-format off + _NODISCARD constexpr auto end() const noexcept(noexcept( + _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ + requires input_range && is_reference_v> { + if constexpr (forward_range && is_reference_v> && forward_range> && common_range && common_range>) { - // clang-format on - return _Iterator{*this, _RANGES end(_Range)}; - } else { - return _Sentinel{*this}; - } + // clang-format on + return _Iterator{*this, _RANGES end(_Range)}; + } else { + return _Sentinel{*this}; } - }; + } + }; - template - join_view(_Rng&&) -> join_view>; + template + join_view(_Rng&&) -> join_view>; - namespace views { - // VARIABLE views::join - class _Join_fn : public _Pipe::_Base<_Join_fn> { - public: - // clang-format off - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept( - join_view>{_STD forward<_Rng>(_Range)})) requires requires { - join_view>{static_cast<_Rng&&>(_Range)}; - } { - // clang-format on - return join_view>{_STD forward<_Rng>(_Range)}; - } - }; + namespace views { + // VARIABLE views::join + class _Join_fn : public _Pipe::_Base<_Join_fn> { + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept( + join_view>{_STD forward<_Rng>(_Range)})) requires requires { + join_view>{static_cast<_Rng&&>(_Range)}; + } { + // clang-format on + return join_view>{_STD forward<_Rng>(_Range)}; + } + }; - inline constexpr _Join_fn join; - } // namespace views + inline constexpr _Join_fn join; // VARIABLE views::counted class _Counted_fn { From 2716fb49e0730261c4dd6c61f5f29ebd1d9e7ab4 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 25 Jan 2021 16:15:43 -0800 Subject: [PATCH 04/55] More formatting cleanup --- stl/inc/ranges | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 21e9cff514d..3c2d499575a 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1008,7 +1008,6 @@ namespace ranges { template <_Copy_constructible_object _Pr> _NODISCARD constexpr auto operator()(_Pr _Pred) const noexcept(is_nothrow_move_constructible_v<_Pr>) { - // clang-format on return _Partial<_Pr>{._Pred = {in_place, _STD move(_Pred)}}; } }; @@ -2501,18 +2500,18 @@ namespace ranges { } // clang-format off - _NODISCARD constexpr _Iterator begin() const noexcept(noexcept( - _RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ - requires input_range && is_reference_v> { + _NODISCARD constexpr _Iterator begin() const noexcept(noexcept( + _RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ + requires input_range && is_reference_v> { // clang-format on return _Iterator{*this, _RANGES begin(_Range)}; } // clang-format off - _NODISCARD constexpr auto end() noexcept(noexcept( - _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { - if constexpr (forward_range<_Vw> && is_reference_v> - && forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { + _NODISCARD constexpr auto end() noexcept(noexcept( + _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { + if constexpr (forward_range<_Vw> && is_reference_v> + && forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { // clang-format on return _Iterator<_Simple_view<_Vw>>{*this, _RANGES end(_Range)}; } else { @@ -2521,12 +2520,12 @@ namespace ranges { } // clang-format off - _NODISCARD constexpr auto end() const noexcept(noexcept( - _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ - requires input_range && is_reference_v> { - if constexpr (forward_range && is_reference_v> - && forward_range> && common_range - && common_range>) { + _NODISCARD constexpr auto end() const noexcept(noexcept( + _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ + requires input_range && is_reference_v> { + if constexpr (forward_range && is_reference_v> + && forward_range> && common_range + && common_range>) { // clang-format on return _Iterator{*this, _RANGES end(_Range)}; } else { @@ -2543,11 +2542,11 @@ namespace ranges { class _Join_fn : public _Pipe::_Base<_Join_fn> { public: // clang-format off - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept( - join_view>{_STD forward<_Rng>(_Range)})) requires requires { - join_view>{static_cast<_Rng&&>(_Range)}; - } { + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept( + join_view>{_STD forward<_Rng>(_Range)})) requires requires { + join_view>{static_cast<_Rng&&>(_Range)}; + } { // clang-format on return join_view>{_STD forward<_Rng>(_Range)}; } From e3e11db213a3fdb5d87df41e5fabcaa0db31eda7 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 25 Jan 2021 16:47:40 -0800 Subject: [PATCH 05/55] Make deduction guide explicit --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 3c2d499575a..2cd715704ca 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2535,7 +2535,7 @@ namespace ranges { }; template - join_view(_Rng&&) -> join_view>; + explicit join_view(_Rng&&) -> join_view>; namespace views { // VARIABLE views::join From d4463ac485bf99545df8527b91c9db8fdd08bde8 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 25 Jan 2021 17:00:26 -0800 Subject: [PATCH 06/55] Add some more wishful [[no_unique_address]] comments --- stl/inc/ranges | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 2cd715704ca..846032d4e15 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2270,8 +2270,8 @@ namespace ranges { static constexpr bool _Deref_is_ref = is_reference_v<_SubRng>; - _OuterIter _Outer{}; - _InnerIter _Inner{}; + /* [[no_unique_address]] */ _OuterIter _Outer{}; + /* [[no_unique_address]] */ _InnerIter _Inner{}; _Parent_t* _Parent{}; constexpr void _Satisfy() { @@ -2451,7 +2451,7 @@ namespace ranges { template using _Maybe_const_iter = iterator_t<_Maybe_const<_OtherConst, _Vw>>; - sentinel_t<_Base> _Last{}; + /* [[no_unique_address]] */ sentinel_t<_Base> _Last{}; template _NODISCARD static constexpr const _Maybe_const_iter<_OtherConst>& _Get_current( From e131afce69295ea88f2b8b48fd8bb8f93caecdd2 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 25 Jan 2021 17:08:27 -0800 Subject: [PATCH 07/55] Nitpicky formatting --- stl/inc/ranges | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 846032d4e15..4f1e327049c 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2301,9 +2301,7 @@ namespace ranges { _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference end join_view iterator"); _STL_VERIFY(_Inner != _RANGES end(_Parent->_Inner), "cannot dereference end join_view iterator"); } -#endif // _ITERATOR_DEBUG_LEVEL != 0 -#if _ITERATOR_DEBUG_LEVEL != 0 constexpr void _Same_range(const _Iterator& _Right) const noexcept { _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible join_view iterators"); } @@ -2345,9 +2343,11 @@ namespace ranges { return *_Inner; } + // clang-format off _NODISCARD constexpr _InnerIter operator->() const // Per resolution of LWG-3500 noexcept(is_nothrow_copy_constructible_v<_InnerIter>) /* strengthened */ - requires _Has_arrow<_InnerIter>&& copyable<_InnerIter> { + requires _Has_arrow<_InnerIter> && copyable<_InnerIter> { + // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 _Check_dereference(); #endif // _ITERATOR_DEBUG_LEVEL != 0 From 55cf9e80ef5821aaa349867a61e58b05beb403f4 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 25 Jan 2021 17:09:30 -0800 Subject: [PATCH 08/55] Coalesce operator++(int) overloads --- stl/inc/ranges | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 4f1e327049c..e6efcd704b4 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2370,17 +2370,14 @@ namespace ranges { } } - constexpr void operator++(int) { - ++*this; - } - - // clang-format off - constexpr decltype(auto) operator++(int) - requires _Deref_is_ref && forward_range<_Base>&& forward_range<_SubRng> { - // clang-format on - auto _Tmp = *this; - ++*this; - return _Tmp; + constexpr auto operator++(int) { + if constexpr (_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>) { + auto _Tmp = *this; + ++*this; + return _Tmp; + } else { + ++*this; + } } // clang-format off From edc0d76389d79c876175886f43178cf482a76438 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 26 Jan 2021 10:40:22 -0800 Subject: [PATCH 09/55] ranges:: -> _RANGES --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index e6efcd704b4..9faadf5416e 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2425,7 +2425,7 @@ namespace ranges { } friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(ranges::iter_swap(_Left._Inner, _Right._Inner))) requires indirectly_swappable<_InnerIter> { + noexcept(_RANGES iter_swap(_Left._Inner, _Right._Inner))) requires indirectly_swappable<_InnerIter> { #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Check_dereference(); _Right._Check_dereference(); From 14fbe654b223799bad01c88501a6299c1ac1ea87 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 27 Jan 2021 15:31:26 -0800 Subject: [PATCH 10/55] I've submitted an LWG issue to constrain join_view::iterator's iter_swap ... as is implemented in this PR. --- stl/inc/ranges | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 9faadf5416e..9b20ada5119 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2424,8 +2424,10 @@ namespace ranges { return _RANGES iter_move(_It._Inner); } - friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_RANGES iter_swap(_Left._Inner, _Right._Inner))) requires indirectly_swappable<_InnerIter> { + friend constexpr void iter_swap( + const _Iterator& _Left, const _Iterator& _Right) noexcept(noexcept(_RANGES iter_swap(_Left._Inner, + _Right + ._Inner))) requires indirectly_swappable<_InnerIter> { // per LWG issue unnumbered as of 2021-01-27 #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Check_dereference(); _Right._Check_dereference(); From cd6793a02581a5412129215389dc3a4397574f90 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 27 Jan 2021 16:41:07 -0800 Subject: [PATCH 11/55] Cleanup noexcept in join_view::sentinel --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 9b20ada5119..60fa6308afe 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2461,7 +2461,7 @@ namespace ranges { public: _Sentinel() = default; // clang-format off - constexpr explicit _Sentinel(_Parent_t& _Parent) noexcept(noexcept(_RANGES end(_STD declval<_Base>())) + constexpr explicit _Sentinel(_Parent_t& _Parent) noexcept(noexcept(_RANGES end(_Parent._Range)) && is_nothrow_move_constructible_v>) // strengthened : _Last(_RANGES end(_Parent._Range)) {} @@ -2473,7 +2473,7 @@ namespace ranges { template requires sentinel_for, _Maybe_const_iter<_OtherConst>> _NODISCARD friend constexpr bool operator==(const _Iterator<_OtherConst>& _Left, - const _Sentinel& _Right) noexcept(noexcept(_Get_current(_Right) == _Last)) /* strengthened */ { + const _Sentinel& _Right) noexcept(noexcept(_Get_current(_Left) == _Right._Last)) /* strengthened */ { // clang-format on return _Get_current(_Left) == _Right._Last; } From 7b20bf0d9638f2cc912ce17e875fdf0d7ddf1a6f Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 27 Jan 2021 17:08:52 -0800 Subject: [PATCH 12/55] join_view only has _Inner when !_Deref_is_ref --- stl/inc/ranges | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 60fa6308afe..7a65e593d6f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2218,13 +2218,29 @@ namespace ranges { // clang-format off template requires view<_Vw> && input_range> - && (is_reference_v> || view>) + && (is_reference_v> || view>) + class join_view; + // clang-format on + + template + class _Join_view_base : public view_interface> { + protected: + /* [[no_unique_address]] */ views::all_t> _Inner{}; + }; + + // clang-format off + template + requires is_reference_v> + class _Join_view_base<_Vw> : public view_interface> {}; + + template + requires view<_Vw>&& input_range> + && (is_reference_v> || view>) class join_view : public view_interface> { // clang-format on private: using _InnerRng = range_reference_t<_Vw>; /* [[no_unique_address]] */ _Vw _Range{}; - /* [[no_unique_address]] */ views::all_t<_InnerRng> _Inner{}; template class _Sentinel; @@ -2299,7 +2315,13 @@ namespace ranges { constexpr void _Check_dereference() const noexcept { _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator"); _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference end join_view iterator"); - _STL_VERIFY(_Inner != _RANGES end(_Parent->_Inner), "cannot dereference end join_view iterator"); + sentinel_t<_InnerRng> _Last; + if constexpr (_Deref_is_ref) { + _Last = _RANGES end(*_Outer); + } else { + _Last = _RANGES end(_Parent->_Inner); + } + _STL_VERIFY(_Inner != _Last, "cannot dereference end join_view iterator"); } constexpr void _Same_range(const _Iterator& _Right) const noexcept { @@ -2360,14 +2382,13 @@ namespace ranges { ++_Outer; _Satisfy(); } - return *this; } else { if (++_Inner == _RANGES end(_Parent->_Inner)) { ++_Outer; _Satisfy(); } - return *this; } + return *this; } constexpr auto operator++(int) { From 13a5b7ecdda3d9bec36afa8443b302591999bd15 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 1 Feb 2021 13:22:48 -0800 Subject: [PATCH 13/55] LWG-3517 is numbered --- stl/inc/ranges | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 7a65e593d6f..9f317ef07fd 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2445,10 +2445,9 @@ namespace ranges { return _RANGES iter_move(_It._Inner); } - friend constexpr void iter_swap( - const _Iterator& _Left, const _Iterator& _Right) noexcept(noexcept(_RANGES iter_swap(_Left._Inner, - _Right - ._Inner))) requires indirectly_swappable<_InnerIter> { // per LWG issue unnumbered as of 2021-01-27 + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_RANGES iter_swap(_Left._Inner, + _Right._Inner))) requires indirectly_swappable<_InnerIter> { // per LWG-3517 #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Check_dereference(); _Right._Check_dereference(); From 5c54a904742cbb828b1c93af60927924ec663406 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 1 Feb 2021 14:20:22 -0800 Subject: [PATCH 14/55] common_type => common_type_t --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 9f317ef07fd..3931dc42ac8 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2336,7 +2336,7 @@ namespace ranges { conditional_t<_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>, forward_iterator_tag, input_iterator_tag>>; using value_type = range_value_t<_SubRng>; - using difference_type = common_type, range_difference_t<_SubRng>>; + using difference_type = common_type_t, range_difference_t<_SubRng>>; _Iterator() = default; From 07d8158026c0a33a8d4a0aa7fa1b4d3d8197f579 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 1 Feb 2021 14:23:40 -0800 Subject: [PATCH 15/55] Fix return types of post-xcrement operators --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 3931dc42ac8..0014ff0ef68 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2391,7 +2391,7 @@ namespace ranges { return *this; } - constexpr auto operator++(int) { + constexpr decltype(auto) operator++(int) { if constexpr (_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>) { auto _Tmp = *this; ++*this; @@ -2417,7 +2417,7 @@ namespace ranges { } // clang-format off - constexpr _Iterator& operator--(int) + constexpr _Iterator operator--(int) requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng> && common_range<_SubRng> { // clang-format on From ecaf90ba2751668707d7a6595d7cfa9e3ad02acd Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 1 Feb 2021 15:18:26 -0800 Subject: [PATCH 16/55] Suppress Clang "unused capture" wanring --- stl/inc/ranges | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/inc/ranges b/stl/inc/ranges index 0014ff0ef68..0b7a718a931 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2293,6 +2293,7 @@ namespace ranges { constexpr void _Satisfy() { auto _Update_inner = [this](_SubRng _Range) -> auto& { if constexpr (_Deref_is_ref) { + (void) this; // Suppress Clang "unused capture" warning return _Range; } else { return (_Parent->_Inner = views::all(_STD move(_Range))); From ab42d29ef83341a5421ed59cb3c61159b6eb912c Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 1 Feb 2021 15:19:01 -0800 Subject: [PATCH 17/55] Use the preprocessor to disable portions of test.cpp instead of `/* ... */` --- tests/std/tests/P0896R4_views_join/test.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 2f5b51f479f..982713b0799 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -35,7 +35,7 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { using V = views::all_t; using R = join_view; static_assert(ranges::view); - /* +#if 0 // FIXME static_assert(input_range == input_range && input_range); static_assert(forward_range == forward_range && forward_range); static_assert(bidirectional_range == bidirectional_range && bidirectional_range); @@ -149,7 +149,7 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { same_as auto r = join_view{forward(rng)}; assert(ranges::equal(r, expected)); - /* +#if 0 // FIXME // Validate join_view::size static_assert(CanMemberSize == sized_range); if constexpr (sized_range) { @@ -289,7 +289,8 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { assert(*prev(b2.end()) == *begin(expected)); } } - */ +#endif // FIXME +#endif // FIXME } return true; } @@ -379,7 +380,7 @@ int main() { static_assert(test_one(input, string_view{}, expected)); test_one(input, string_view{}, expected); } - /* +#if 0 // FIXME { // ... move-only test_one(move_only_view{some_ints}, joind_ints); test_one(move_only_view{some_ints}, joind_ints); @@ -418,5 +419,5 @@ int main() { // Get full instantiation coverage static_assert((test_nested_inout(), true)); test_nested_inout(); - */ +#endif // FIXME } From 745f3631be6889d7237e72ad3a17125afe420df8 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 1 Feb 2021 15:21:41 -0800 Subject: [PATCH 18/55] Remove `pipeline` from test; we've got enough tests of the pipelining machinery --- tests/std/tests/P0896R4_views_join/test.cpp | 29 +++------------------ 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 982713b0799..fd63b359018 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -20,9 +20,6 @@ concept CanViewJoin = requires(Rng&& r) { views::join(static_cast(r)); }; -// Test a silly precomposed range adaptor pipeline -constexpr auto pipeline = views::all | views::join; - template constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { using ranges::begin, ranges::bidirectional_range, ranges::common_range, ranges::enable_borrowed_range, ranges::end, @@ -35,7 +32,10 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { using V = views::all_t; using R = join_view; static_assert(ranges::view); -#if 0 // FIXME +#if 1 // FIXME + (void) rng; + (void) expected; +#else // FIXME static_assert(input_range == input_range && input_range); static_assert(forward_range == forward_range && forward_range); static_assert(bidirectional_range == bidirectional_range && bidirectional_range); @@ -53,9 +53,6 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { static_assert(same_as); static_assert(noexcept(rng | views::join) == is_noexcept); - - static_assert(same_as); - static_assert(noexcept(rng | pipeline) == is_noexcept); } // ... with const lvalue argument @@ -68,9 +65,6 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { static_assert(same_as); static_assert(noexcept(as_const(rng) | views::join) == is_noexcept); - - static_assert(same_as); - static_assert(noexcept(as_const(rng) | pipeline) == is_noexcept); } else if constexpr (!is_view) { using RC = join_view&>>; constexpr bool is_noexcept = is_nothrow_constructible_v&>; @@ -80,9 +74,6 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { static_assert(same_as); static_assert(noexcept(as_const(rng) | views::join) == is_noexcept); - - static_assert(same_as); - static_assert(noexcept(as_const(rng) | pipeline) == is_noexcept); } // ... with rvalue argument @@ -95,9 +86,6 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { static_assert(same_as); static_assert(noexcept(move(rng) | views::join) == 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{move(rng)}); using RS = join_view; @@ -108,9 +96,6 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { static_assert(same_as); static_assert(noexcept(move(rng) | views::join) == is_noexcept); - - static_assert(same_as); - static_assert(noexcept(move(rng) | pipeline) == is_noexcept); } // ... with const rvalue argument @@ -124,9 +109,6 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { static_assert(same_as); static_assert(noexcept(move(as_const(rng)) | views::join) == is_nothrow_copy_constructible_v); - - 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{as_const(rng)}); using RS = join_view; @@ -137,9 +119,6 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { static_assert(same_as); static_assert(noexcept(move(as_const(rng)) | views::join) == is_noexcept); - - static_assert(same_as); - static_assert(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); } // Validate deduction guide From 212e34344dc8c63d1d965c63b9018a24a7a6dd1d Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 1 Feb 2021 15:28:31 -0800 Subject: [PATCH 19/55] Wiggle --- tests/std/tests/P0896R4_views_join/test.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index fd63b359018..d8c57c0c3d0 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -129,7 +129,7 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { assert(ranges::equal(r, expected)); #if 0 // FIXME - // Validate join_view::size + // Validate join_view::size static_assert(CanMemberSize == sized_range); if constexpr (sized_range) { assert(r.size() == static_cast>(size(expected))); @@ -257,9 +257,9 @@ constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { } // Validate join_view::base() && (NB: do this last since it leaves r moved-from) - #if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 (void) 42; - #endif // TRANSITION, DevCom-1159442 +#endif // TRANSITION, DevCom-1159442 same_as auto b2 = move(r).base(); static_assert(noexcept(move(r).base()) == is_nothrow_move_constructible_v); if (!is_empty) { @@ -353,8 +353,8 @@ using move_only_view = test::range data = {{{}, "Hello "sv, {}, "World!"sv, {}}}; - span input{data}; + static constexpr array data = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + constexpr span input{data}; constexpr string_view expected = "Hello World!"sv; static_assert(test_one(input, string_view{}, expected)); test_one(input, string_view{}, expected); From f6c37119bec9104f241f128b1d8ac361adfd062d Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 2 Feb 2021 11:54:33 -0800 Subject: [PATCH 20/55] Rename _Deref_is_ref to _Deref_is_glvalue The fully-expanded name is "Dereference expression is a glvalue" Use strict_concepts_matrix because iter_move. --- stl/inc/ranges | 35 +++++++-------- tests/std/tests/P0896R4_views_join/env.lst | 2 +- tests/std/tests/P0896R4_views_join/test.cpp | 47 +++++++++++---------- 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 0b7a718a931..d3ba6f27588 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2245,19 +2245,19 @@ namespace ranges { template class _Sentinel; - template // TRANSITION, LWG-3289 + template // TRANSITION, LWG-3289 struct _Category_base {}; // clang-format off - template + template requires _Has_member_iterator_category<_InnerTraits> && _Has_member_iterator_category<_OuterTraits> - struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_ref> { + struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_glvalue> { using iterator_category = - conditional_t<_Deref_is_ref + conditional_t<_Deref_is_glvalue && derived_from && derived_from, bidirectional_iterator_tag, - conditional_t<_Deref_is_ref + conditional_t<_Deref_is_glvalue && derived_from && derived_from, forward_iterator_tag, @@ -2284,7 +2284,8 @@ namespace ranges { using _OuterIter = iterator_t<_Base>; using _InnerIter = iterator_t<_SubRng>; - static constexpr bool _Deref_is_ref = is_reference_v<_SubRng>; + // True if and only if the expression *i, where i is an iterator from the outer range, is a glvalue: + static constexpr bool _Deref_is_glvalue = is_reference_v<_SubRng>; /* [[no_unique_address]] */ _OuterIter _Outer{}; /* [[no_unique_address]] */ _InnerIter _Inner{}; @@ -2292,7 +2293,7 @@ namespace ranges { constexpr void _Satisfy() { auto _Update_inner = [this](_SubRng _Range) -> auto& { - if constexpr (_Deref_is_ref) { + if constexpr (_Deref_is_glvalue) { (void) this; // Suppress Clang "unused capture" warning return _Range; } else { @@ -2307,7 +2308,7 @@ namespace ranges { return; } } - if constexpr (_Deref_is_ref) { + if constexpr (_Deref_is_glvalue) { _Inner = _InnerIter{}; } } @@ -2317,7 +2318,7 @@ namespace ranges { _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator"); _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference end join_view iterator"); sentinel_t<_InnerRng> _Last; - if constexpr (_Deref_is_ref) { + if constexpr (_Deref_is_glvalue) { _Last = _RANGES end(*_Outer); } else { _Last = _RANGES end(_Parent->_Inner); @@ -2332,10 +2333,10 @@ namespace ranges { public: using iterator_concept = - conditional_t<_Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng>, + conditional_t<_Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_SubRng>, bidirectional_iterator_tag, - conditional_t<_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>, forward_iterator_tag, - input_iterator_tag>>; + conditional_t<_Deref_is_glvalue && forward_range<_Base> && forward_range<_SubRng>, + forward_iterator_tag, input_iterator_tag>>; using value_type = range_value_t<_SubRng>; using difference_type = common_type_t, range_difference_t<_SubRng>>; @@ -2378,7 +2379,7 @@ namespace ranges { } constexpr _Iterator& operator++() { - if constexpr (_Deref_is_ref) { + if constexpr (_Deref_is_glvalue) { if (++_Inner == _RANGES end(*_Outer)) { ++_Outer; _Satisfy(); @@ -2393,7 +2394,7 @@ namespace ranges { } constexpr decltype(auto) operator++(int) { - if constexpr (_Deref_is_ref && forward_range<_Base> && forward_range<_SubRng>) { + if constexpr (_Deref_is_glvalue && forward_range<_Base> && forward_range<_SubRng>) { auto _Tmp = *this; ++*this; return _Tmp; @@ -2404,7 +2405,7 @@ namespace ranges { // clang-format off constexpr _Iterator& operator--() - requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng> + requires _Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_SubRng> && common_range<_SubRng> { // clang-format on if (_Outer == _RANGES end(_Parent->_Range)) { @@ -2419,7 +2420,7 @@ namespace ranges { // clang-format off constexpr _Iterator operator--(int) - requires _Deref_is_ref && bidirectional_range<_Base> && bidirectional_range<_SubRng> + requires _Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_SubRng> && common_range<_SubRng> { // clang-format on auto _Tmp = *this; @@ -2430,7 +2431,7 @@ namespace ranges { // clang-format off _NODISCARD friend constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( noexcept(_Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner)) /* strengthened */ - requires _Deref_is_ref && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { + requires _Deref_is_glvalue && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Same_range(_Right); diff --git a/tests/std/tests/P0896R4_views_join/env.lst b/tests/std/tests/P0896R4_views_join/env.lst index f3ccc8613c6..62a24024479 100644 --- a/tests/std/tests/P0896R4_views_join/env.lst +++ b/tests/std/tests/P0896R4_views_join/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\concepts_matrix.lst +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index d8c57c0c3d0..1b3c36937f5 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -20,31 +20,34 @@ concept CanViewJoin = requires(Rng&& r) { views::join(static_cast(r)); }; -template -constexpr bool test_one(Outer&& rng, Inner&&, Expected&& expected) { +template +constexpr bool test_one(Outer&& rng, Expected&& expected) { using ranges::begin, ranges::bidirectional_range, ranges::common_range, ranges::enable_borrowed_range, ranges::end, ranges::forward_range, ranges::input_range, ranges::iterator_t, ranges::join_view, ranges::range_value_t; - constexpr bool deref_is_reference = is_reference_v>; - constexpr bool is_view = ranges::view>; + using Inner = range_value_t; + static_assert(ranges::range); - if constexpr (deref_is_reference || is_view) { + constexpr bool deref_is_glvalue = is_reference_v>; + + if constexpr (deref_is_glvalue || ranges::view) { using V = views::all_t; using R = join_view; static_assert(ranges::view); -#if 1 // FIXME - (void) rng; - (void) expected; -#else // FIXME - static_assert(input_range == input_range && input_range); + static_assert(input_range == input_range); static_assert(forward_range == forward_range && forward_range); static_assert(bidirectional_range == bidirectional_range && bidirectional_range); static_assert(!ranges::random_access_range); static_assert(!ranges::contiguous_range); - // Validate range adapter object - // ...with lvalue argument - static_assert(CanViewJoin == (!is_view || copyable) ); +#if 1 // FIXME + (void) rng; + (void) expected; +#else // FIXME + // Validate range adapter object + // ...with lvalue argument + static_assert( + CanViewJoin == (!is_view || copyable) ); // FIXME: This "is_view" is different from line 32. if constexpr (CanViewJoin) { constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; @@ -329,19 +332,17 @@ constexpr void test_nested_inout() { with_input_or_output_ranges, Element>::call(); } -static constexpr int full_range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; -static constexpr span first_range{full_range, full_range + 3}; -static constexpr span second_range{full_range + 3, full_range + 7}; -static constexpr span third_range{full_range + 7, full_range + 7}; // NB: Intentionally left empty -static constexpr span fourth_range{full_range + 7, full_range + 10}; +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +static constexpr span intervals[] = {{some_ints, some_ints + 3}, {some_ints + 3, some_ints + 7}, + {some_ints + 7, some_ints + 7}, {some_ints + 7, some_ints + 10}}; struct instantiator { template static constexpr void call() { - array inner_ranges = { - Inner{first_range}, Inner{second_range}, Inner{third_range}, Inner{fourth_range}}; + static_assert(ranges::size(intervals) == 4); + array inner_ranges = {Inner{intervals[0]}, Inner{intervals[1]}, Inner{intervals[2]}, Inner{intervals[3]}}; Outer r{inner_ranges}; - test_one(r, first_range, full_range); + test_one(r, some_ints); } }; @@ -356,8 +357,8 @@ int main() { static constexpr array data = {{{}, "Hello "sv, {}, "World!"sv, {}}}; constexpr span input{data}; constexpr string_view expected = "Hello World!"sv; - static_assert(test_one(input, string_view{}, expected)); - test_one(input, string_view{}, expected); + static_assert(test_one(input, expected)); + test_one(input, expected); } #if 0 // FIXME { // ... move-only From b07eed913f208fd62fc295183fe8aff864dba761 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 2 Feb 2021 13:36:45 -0800 Subject: [PATCH 21/55] Expose more test surface --- tests/std/tests/P0896R4_views_join/test.cpp | 66 ++++++++------------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 1b3c36937f5..6ae7fcc493a 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -40,14 +40,11 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { static_assert(!ranges::random_access_range); static_assert(!ranges::contiguous_range); -#if 1 // FIXME - (void) rng; - (void) expected; -#else // FIXME - // Validate range adapter object - // ...with lvalue argument - static_assert( - CanViewJoin == (!is_view || copyable) ); // FIXME: This "is_view" is different from line 32. + constexpr bool is_view = ranges::view; + + // Validate range adapter object + // ...with lvalue argument + static_assert(CanViewJoin == (!is_view || copyable) ); if constexpr (CanViewJoin) { constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; @@ -108,10 +105,10 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v; static_assert(same_as); - static_assert(noexcept(views::join(move(as_const(rng)))) == is_nothrow_copy_constructible_v); + static_assert(noexcept(views::join(move(as_const(rng)))) == is_noexcept); static_assert(same_as); - static_assert(noexcept(move(as_const(rng)) | views::join) == is_nothrow_copy_constructible_v); + static_assert(noexcept(move(as_const(rng)) | views::join) == is_noexcept); } else if constexpr (!is_view && enable_borrowed_range>) { using S = decltype(ranges::subrange{as_const(rng)}); using RS = join_view; @@ -125,47 +122,40 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } // Validate deduction guide -#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 - (void) 42; -#endif // TRANSITION, DevCom-1159442 same_as auto r = join_view{forward(rng)}; assert(ranges::equal(r, expected)); -#if 0 // FIXME - // Validate join_view::size - static_assert(CanMemberSize == sized_range); - if constexpr (sized_range) { - assert(r.size() == static_cast>(size(expected))); - static_assert(noexcept(r.size()) == noexcept(size(rng))); - } - - static_assert(CanMemberSize == sized_range); - if constexpr (sized_range) { - assert(as_const(r).size() == static_cast>(size(expected))); - static_assert(noexcept(r.size()) == noexcept(size(as_const(rng)))); - } + // Validate join_view::size + static_assert(!CanSize); // Validate view_interface::empty and operator bool - const bool is_empty = ranges::empty(expected); - assert(r.empty() == is_empty); - assert(static_cast(r) == !is_empty); - static_assert(CanMemberEmpty == common_range); - if constexpr (common_range) { - assert(as_const(r).empty() == is_empty); - assert(static_cast(as_const(r)) == !is_empty); + static_assert(CanEmpty == forward_range); + static_assert(CanMemberEmpty == CanEmpty); + if (forward_range) { + const bool is_empty = ranges::empty(expected); + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + + static_assert(CanEmpty == forward_range); + static_assert(CanMemberEmpty == CanEmpty); + if constexpr (forward_range) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } } - // Validate join_view::begin +#if 0 // FIXME + // Validate join_view::begin static_assert(CanMemberBegin); { - // join_view sometimes caches begin, so let's make several extra calls + // join_view sometimes caches begin, so let's make several extra calls // FIXME: lies const same_as>> auto i = r.begin(); if (!is_empty) { assert(*i == *begin(expected)); } assert(r.begin() == i); assert(r.begin() == i); - // NB: non-const begin is unconditionally noexcept(false) due to caching + // NB: non-const begin is unconditionally noexcept(false) due to caching // FIXME: lies static_assert(!noexcept(r.begin())); if constexpr (copyable) { @@ -260,9 +250,6 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } // Validate join_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 same_as auto b2 = move(r).base(); static_assert(noexcept(move(r).base()) == is_nothrow_move_constructible_v); if (!is_empty) { @@ -271,7 +258,6 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { assert(*prev(b2.end()) == *begin(expected)); } } -#endif // FIXME #endif // FIXME } return true; From f7fabeebba299e349be574c8a5d3f34696111741 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 2 Feb 2021 13:41:05 -0800 Subject: [PATCH 22/55] Cleanup definition of `intervals` --- tests/std/tests/P0896R4_views_join/test.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 6ae7fcc493a..178eb1a0358 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -319,16 +319,19 @@ constexpr void test_nested_inout() { } static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; -static constexpr span intervals[] = {{some_ints, some_ints + 3}, {some_ints + 3, some_ints + 7}, - {some_ints + 7, some_ints + 7}, {some_ints + 7, some_ints + 10}}; +static constexpr span intervals[] = { + {some_ints + 0, some_ints + 3}, + {some_ints + 3, some_ints + 7}, + {some_ints + 7, some_ints + 7}, + {some_ints + 7, some_ints + 10}, +}; struct instantiator { template static constexpr void call() { static_assert(ranges::size(intervals) == 4); array inner_ranges = {Inner{intervals[0]}, Inner{intervals[1]}, Inner{intervals[2]}, Inner{intervals[3]}}; - Outer r{inner_ranges}; - test_one(r, some_ints); + test_one(Outer{inner_ranges}, some_ints); } }; From c2412a1ae6c0e59794c9b46ee480dfc5d5e2deb1 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 2 Feb 2021 16:17:23 -0800 Subject: [PATCH 23/55] joind => joined --- tests/std/tests/P0896R4_views_join/test.cpp | 29 +++++++++++---------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 178eb1a0358..579b41fba39 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -351,18 +351,18 @@ int main() { } #if 0 // FIXME { // ... move-only - test_one(move_only_view{some_ints}, joind_ints); - test_one(move_only_view{some_ints}, joind_ints); - test_one(move_only_view{some_ints}, joind_ints); - test_one(move_only_view{some_ints}, joind_ints); - test_one(move_only_view{some_ints}, joind_ints); - test_one(move_only_view{some_ints}, joind_ints); + test_one(move_only_view{some_ints}, joined_ints); + test_one(move_only_view{some_ints}, joined_ints); + test_one(move_only_view{some_ints}, joined_ints); + test_one(move_only_view{some_ints}, joined_ints); + test_one(move_only_view{some_ints}, joined_ints); + test_one(move_only_view{some_ints}, joined_ints); } // Validate non-views { // ... C array - static_assert(test_one(some_ints, joind_ints)); - test_one(some_ints, joind_ints); + static_assert(test_one(some_ints, joined_ints)); + test_one(some_ints, joined_ints); } { // ... contiguous container string str{"Hello, World!"}; @@ -371,20 +371,21 @@ int main() { } { // ... bidi container list lst{3, 4, 5}; - static constexpr int joind[] = {5, 4, 3}; - test_one(lst, joind); + static constexpr int joined[] = {5, 4, 3}; + test_one(lst, joined); - static constexpr int joind_prefix[] = {4, 3}; + static constexpr int joined_prefix[] = {4, 3}; assert(ranges::equal( - views::join(ranges::subrange{counted_iterator{lst.begin(), 2}, default_sentinel}), joind_prefix)); + views::join(ranges::subrange{counted_iterator{lst.begin(), 2}, default_sentinel}), joined_prefix)); } // Validate a non-view borrowed range { constexpr span s{some_ints}; - static_assert(test_one(s, joind_ints)); - test_one(s, joind_ints); + static_assert(test_one(s, joined_ints)); + test_one(s, joined_ints); } + // Get full instantiation coverage static_assert((test_nested_inout(), true)); test_nested_inout(); From 6fc264ea84b0973675b8d10a622fcd720f1603b1 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 11 Feb 2021 15:22:25 -0800 Subject: [PATCH 24/55] Implement sentinel comparison --- stl/inc/ranges | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index d3ba6f27588..1ca3cc7512a 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2474,10 +2474,13 @@ namespace ranges { /* [[no_unique_address]] */ sentinel_t<_Base> _Last{}; + // clang-format off template - _NODISCARD static constexpr const _Maybe_const_iter<_OtherConst>& _Get_current( - const _Iterator<_OtherConst>& _It) noexcept { - return _It._Current; + requires sentinel_for, _Maybe_const_iter<_OtherConst>> + _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const noexcept( + noexcept(_It._Outer == _Last)) { + // clang-format on + return _It._Outer == _Last; } public: @@ -2495,9 +2498,9 @@ namespace ranges { template requires sentinel_for, _Maybe_const_iter<_OtherConst>> _NODISCARD friend constexpr bool operator==(const _Iterator<_OtherConst>& _Left, - const _Sentinel& _Right) noexcept(noexcept(_Get_current(_Left) == _Right._Last)) /* strengthened */ { + const _Sentinel& _Right) noexcept(noexcept(_Right._Equal(_Left))) /* strengthened */ { // clang-format on - return _Get_current(_Left) == _Right._Last; + return _Right._Equal(_Left); } }; From 766f3c1f8f0171f53d2928b60ac3ef4996c3b6ac Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 11 Feb 2021 15:22:34 -0800 Subject: [PATCH 25/55] test WIP --- tests/std/tests/P0896R4_views_join/test.cpp | 80 +++++++++++---------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 579b41fba39..2676b69dada 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -25,18 +25,22 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { using ranges::begin, ranges::bidirectional_range, ranges::common_range, ranges::enable_borrowed_range, ranges::end, ranges::forward_range, ranges::input_range, ranges::iterator_t, ranges::join_view, ranges::range_value_t; - using Inner = range_value_t; - static_assert(ranges::range); - + using Inner = range_value_t; constexpr bool deref_is_glvalue = is_reference_v>; - if constexpr (deref_is_glvalue || ranges::view) { + // clang-format off + constexpr bool can_test = ranges::viewable_range + && ranges::input_range> + && (deref_is_glvalue || ranges::view); + // clang-format on + + if constexpr (can_test) { using V = views::all_t; using R = join_view; static_assert(ranges::view); static_assert(input_range == input_range); - static_assert(forward_range == forward_range && forward_range); - static_assert(bidirectional_range == bidirectional_range && bidirectional_range); + static_assert(forward_range == (forward_range && forward_range) ); + static_assert(bidirectional_range == (bidirectional_range && bidirectional_range) ); static_assert(!ranges::random_access_range); static_assert(!ranges::contiguous_range); @@ -125,20 +129,20 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { same_as auto r = join_view{forward(rng)}; assert(ranges::equal(r, expected)); - // Validate join_view::size + // Validate lack of size static_assert(!CanSize); // Validate view_interface::empty and operator bool - static_assert(CanEmpty == forward_range); + static_assert(CanEmpty == forward_range); static_assert(CanMemberEmpty == CanEmpty); - if (forward_range) { + if constexpr (CanMemberEmpty) { const bool is_empty = ranges::empty(expected); assert(r.empty() == is_empty); assert(static_cast(r) == !is_empty); - static_assert(CanEmpty == forward_range); + static_assert(CanEmpty == forward_range); static_assert(CanMemberEmpty == CanEmpty); - if constexpr (forward_range) { + if constexpr (CanMemberEmpty) { assert(as_const(r).empty() == is_empty); assert(static_cast(as_const(r)) == !is_empty); } @@ -318,12 +322,12 @@ constexpr void test_nested_inout() { with_input_or_output_ranges, Element>::call(); } -static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; -static constexpr span intervals[] = { - {some_ints + 0, some_ints + 3}, - {some_ints + 3, some_ints + 7}, - {some_ints + 7, some_ints + 7}, - {some_ints + 7, some_ints + 10}, +constexpr int expected_ints[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +constexpr span intervals[] = { + {expected_ints + 0, expected_ints + 3}, + {expected_ints + 3, expected_ints + 7}, + {expected_ints + 7, expected_ints + 7}, + {expected_ints + 7, expected_ints + 10}, }; struct instantiator { @@ -331,14 +335,17 @@ struct instantiator { static constexpr void call() { static_assert(ranges::size(intervals) == 4); array inner_ranges = {Inner{intervals[0]}, Inner{intervals[1]}, Inner{intervals[2]}, Inner{intervals[3]}}; - test_one(Outer{inner_ranges}, some_ints); + test_one(Outer{inner_ranges}, expected_ints); } }; +using mo_inner = test::range; + template > -using move_only_view = test::range}, - test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; +using move_only_view = test::range}, test::ProxyRef::no, test::CanView::yes, + test::Copyability::move_only>; int main() { // Validate views @@ -349,21 +356,25 @@ int main() { static_assert(test_one(input, expected)); test_one(input, expected); } -#if 0 // FIXME { // ... move-only - test_one(move_only_view{some_ints}, joined_ints); - test_one(move_only_view{some_ints}, joined_ints); - test_one(move_only_view{some_ints}, joined_ints); - test_one(move_only_view{some_ints}, joined_ints); - test_one(move_only_view{some_ints}, joined_ints); - test_one(move_only_view{some_ints}, joined_ints); + mo_inner data[] = { + mo_inner{intervals[0]}, mo_inner{intervals[1]}, mo_inner{intervals[2]}, mo_inner{intervals[3]}}; + test_one(move_only_view{data}, expected_ints); + test_one(move_only_view{data}, expected_ints); + test_one(move_only_view{data}, expected_ints); + test_one(move_only_view{data}, expected_ints); + test_one(move_only_view{data}, expected_ints); } // Validate non-views +#if defined(__clang__) || defined(__EDG__) // TRANSITION, FIXME { // ... C array - static_assert(test_one(some_ints, joined_ints)); - test_one(some_ints, joined_ints); + static constexpr int join_me[5][2] = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}}; + static_assert(test_one(join_me, expected_ints)); + test_one(join_me, expected_ints); } +#endif // TRANSITION, FIXME +#if 0 // FIXME { // ... contiguous container string str{"Hello, World!"}; constexpr auto expected = "!dlroW ,olleH"sv; @@ -378,14 +389,9 @@ int main() { assert(ranges::equal( views::join(ranges::subrange{counted_iterator{lst.begin(), 2}, default_sentinel}), joined_prefix)); } +#endif // FIXME - // Validate a non-view borrowed range - { - constexpr span s{some_ints}; - static_assert(test_one(s, joined_ints)); - test_one(s, joined_ints); - } - +#if defined(__clang__) || defined(__EDG__) // FIXME: C1060 "out of heap space" // Get full instantiation coverage static_assert((test_nested_inout(), true)); test_nested_inout(); From 8a550ef698a8c24fa7ce0ed971e02f3f34372911 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 11 Feb 2021 15:40:49 -0800 Subject: [PATCH 26/55] Don't reuse inner ranges in the move-only view test --- tests/std/tests/P0896R4_views_join/test.cpp | 31 +++++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 2676b69dada..255f61ad635 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -346,6 +346,26 @@ template }, test::ProxyRef::no, test::CanView::yes, test::Copyability::move_only>; +void test_move_only_views() { + const auto gen = [] { + return array{mo_inner{intervals[0]}, mo_inner{intervals[1]}, mo_inner{intervals[2]}, mo_inner{intervals[3]}}; + }; + + auto data = gen(); + test_one(move_only_view{data}, expected_ints); + + data = gen(); + test_one(move_only_view{data}, expected_ints); + + data = gen(); + test_one(move_only_view{data}, expected_ints); + + data = gen(); + test_one(move_only_view{data}, expected_ints); + + data = gen(); + test_one(move_only_view{data}, expected_ints); +} int main() { // Validate views @@ -356,15 +376,8 @@ int main() { static_assert(test_one(input, expected)); test_one(input, expected); } - { // ... move-only - mo_inner data[] = { - mo_inner{intervals[0]}, mo_inner{intervals[1]}, mo_inner{intervals[2]}, mo_inner{intervals[3]}}; - test_one(move_only_view{data}, expected_ints); - test_one(move_only_view{data}, expected_ints); - test_one(move_only_view{data}, expected_ints); - test_one(move_only_view{data}, expected_ints); - test_one(move_only_view{data}, expected_ints); - } + // ... move-only + test_move_only_views(); // Validate non-views #if defined(__clang__) || defined(__EDG__) // TRANSITION, FIXME From 58c24d2bba1bac798735fba1b326c8cf0e7530e0 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 12 Feb 2021 14:29:30 -0800 Subject: [PATCH 27/55] more test WIP --- tests/std/tests/P0896R4_views_join/test.cpp | 44 ++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 255f61ad635..02563fcd2ec 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -128,6 +128,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate deduction guide same_as auto r = join_view{forward(rng)}; assert(ranges::equal(r, expected)); + const bool is_empty = ranges::empty(expected); // Validate lack of size static_assert(!CanSize); @@ -136,7 +137,6 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { static_assert(CanEmpty == forward_range); static_assert(CanMemberEmpty == CanEmpty); if constexpr (CanMemberEmpty) { - const bool is_empty = ranges::empty(expected); assert(r.empty() == is_empty); assert(static_cast(r) == !is_empty); @@ -148,44 +148,43 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } -#if 0 // FIXME - // Validate join_view::begin + // Validate join_view::begin static_assert(CanMemberBegin); - { - // join_view sometimes caches begin, so let's make several extra calls // FIXME: lies - const same_as>> auto i = r.begin(); + if constexpr (forward_range) { + const iterator_t i = r.begin(); + assert(r.begin() == i); if (!is_empty) { assert(*i == *begin(expected)); } - assert(r.begin() == i); - assert(r.begin() == i); - // NB: non-const begin is unconditionally noexcept(false) due to caching // FIXME: lies - static_assert(!noexcept(r.begin())); if constexpr (copyable) { - auto r2 = r; - const same_as>> auto i2 = r2.begin(); - assert(r2.begin() == i2); + auto r2 = r; + const same_as> auto i2 = r2.begin(); assert(r2.begin() == i2); if (!is_empty) { assert(*i2 == *i); } } - static_assert(CanMemberBegin == common_range); - if constexpr (common_range) { - const same_as>> auto ci = as_const(r).begin(); - assert(as_const(r).begin() == ci); + static_assert( + CanMemberBegin< + const R> == (input_range && is_reference_v>) ); + if constexpr (CanMemberBegin && !CanBegin) { + static_assert(input_iterator().begin())>); + static_assert( + sentinel_for().end()), decltype(std::declval().begin())>); + } + static_assert(CanBegin == CanMemberBegin); + if constexpr (CanBegin && forward_range) { + const iterator_t ci = as_const(r).begin(); assert(as_const(r).begin() == ci); if (!is_empty) { assert(*ci == *i); } - static_assert(noexcept(as_const(r).begin()) == noexcept(join_iterator{end(as_const(rng))})); if constexpr (copyable) { - const auto r2 = r; - const same_as>> auto ci2 = r2.begin(); - assert(r2.begin() == ci2); + const auto r2 = r; + const same_as> auto ci2 = r2.begin(); assert(r2.begin() == ci2); if (!is_empty) { assert(*ci2 == *i); @@ -194,7 +193,8 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } - // Validate join_view::end +#if 0 // FIXME + // Validate join_view::end static_assert(CanMemberEnd); if (!is_empty) { assert(*prev(r.end()) == *prev(end(expected))); From c1693b2aac14bbc4ae463436e1f1a7993b996a06 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 20 Feb 2021 10:45:43 +0100 Subject: [PATCH 28/55] Use _Join_view_base --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 1ca3cc7512a..a65515280f9 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2236,7 +2236,7 @@ namespace ranges { template requires view<_Vw>&& input_range> && (is_reference_v> || view>) - class join_view : public view_interface> { + class join_view : public _Join_view_base<_Vw> { // clang-format on private: using _InnerRng = range_reference_t<_Vw>; From 7b9a17785274c8dd9009cda5d1ea5801812b9d31 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 20 Feb 2021 10:45:56 +0100 Subject: [PATCH 29/55] Enable join_view::end tests --- tests/std/tests/P0896R4_views_join/test.cpp | 50 ++++++++++++++------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 02563fcd2ec..90bcf02156d 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -23,14 +23,15 @@ concept CanViewJoin = requires(Rng&& r) { template constexpr bool test_one(Outer&& rng, Expected&& expected) { using ranges::begin, ranges::bidirectional_range, ranges::common_range, ranges::enable_borrowed_range, ranges::end, - ranges::forward_range, ranges::input_range, ranges::iterator_t, ranges::join_view, ranges::range_value_t; + ranges::forward_range, ranges::input_range, ranges::iterator_t, ranges::join_view, ranges::range_value_t, + ranges::range_reference_t; using Inner = range_value_t; - constexpr bool deref_is_glvalue = is_reference_v>; + constexpr bool deref_is_glvalue = is_reference_v>; // clang-format off constexpr bool can_test = ranges::viewable_range - && ranges::input_range> + && ranges::input_range> && (deref_is_glvalue || ranges::view); // clang-format on @@ -150,6 +151,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::begin static_assert(CanMemberBegin); + static_assert(CanMemberBegin == (input_range && is_reference_v>) ); if constexpr (forward_range) { const iterator_t i = r.begin(); assert(r.begin() == i); @@ -166,15 +168,12 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } - static_assert( - CanMemberBegin< - const R> == (input_range && is_reference_v>) ); if constexpr (CanMemberBegin && !CanBegin) { static_assert(input_iterator().begin())>); static_assert( sentinel_for().end()), decltype(std::declval().begin())>); } - static_assert(CanBegin == CanMemberBegin); + if constexpr (CanBegin && forward_range) { const iterator_t ci = as_const(r).begin(); assert(as_const(r).begin() == ci); @@ -193,24 +192,43 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } -#if 0 // FIXME - // Validate join_view::end + // Validate join_view::end static_assert(CanMemberEnd); + static_assert(CanMemberEnd == (input_range && is_reference_v>) ); + // clang-format off + static_assert(common_range == (forward_range && is_reference_v> && common_range + && forward_range && common_range) ); + static_assert(common_range == (forward_range && is_reference_v> + && common_range && forward_range> + && common_range>) ); + // clang-format on if (!is_empty) { - assert(*prev(r.end()) == *prev(end(expected))); + const ranges::sentinel_t s = r.end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(s) == *prev(end(expected))); + } - if constexpr (copyable) { + if constexpr (bidirectional_range && common_range && copyable) { auto r2 = r; assert(*prev(r2.end()) == *prev(end(expected))); } - static_assert(noexcept(r.end()) == noexcept(join_iterator{begin(rng)})); - static_assert(CanMemberEnd == common_range); - if constexpr (common_range) { - assert(*prev(as_const(r).end()) == *prev(end(expected))); - static_assert(noexcept(as_const(r).end()) == noexcept(join_iterator{begin(as_const(rng))})); + if constexpr (CanMemberEnd) { + const ranges::sentinel_t cs = as_const(r).end(); + if constexpr (bidirectional_range) { + assert(*prev(cs) == *prev(end(expected))); + } + + if constexpr (copyable) { + const auto r2 = r; + const ranges::sentinel_t cs2 = r2.end(); + if constexpr (bidirectional_range) { + assert(*prev(cs2) == *prev(end(expected))); + } + } } } +#if 0 // FIXME // Validate view_interface::data static_assert(!CanData); From 39f825439cc4e0ddd0bb981a3f7efb9a1c7958fa Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 20 Feb 2021 10:52:31 +0100 Subject: [PATCH 30/55] join_view is not random access so nodata and operator[] --- tests/std/tests/P0896R4_views_join/test.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 90bcf02156d..a56a52ef2b3 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -23,8 +23,8 @@ concept CanViewJoin = requires(Rng&& r) { template constexpr bool test_one(Outer&& rng, Expected&& expected) { using ranges::begin, ranges::bidirectional_range, ranges::common_range, ranges::enable_borrowed_range, ranges::end, - ranges::forward_range, ranges::input_range, ranges::iterator_t, ranges::join_view, ranges::range_value_t, - ranges::range_reference_t; + ranges::forward_range, ranges::input_range, ranges::iterator_t, ranges::join_view, ranges::random_access_range, + ranges::range_value_t, ranges::range_reference_t; using Inner = range_value_t; constexpr bool deref_is_glvalue = is_reference_v>; @@ -228,16 +228,16 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } } -#if 0 // FIXME - // Validate view_interface::data static_assert(!CanData); static_assert(!CanData); + // Validate view_interface::operator[] + static_assert(!CanIndex); + static_assert(!CanIndex); +#if 0 // FIXME + if (!is_empty) { - // Validate view_interface::operator[] - static_assert(CanIndex == random_access_range); - static_assert(CanIndex == (random_access_range && common_range) ); if constexpr (random_access_range) { assert(r[0] == *begin(expected)); From 1524233ca0a4b942964f58724dfb5ee8dc0ff1b1 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 20 Feb 2021 11:14:39 +0100 Subject: [PATCH 31/55] WIP join_view::front --- tests/std/tests/P0896R4_views_join/test.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index a56a52ef2b3..f046316f3d6 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -235,8 +235,25 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate view_interface::operator[] static_assert(!CanIndex); static_assert(!CanIndex); + + // Validate view_interface::front and back + static_assert(CanMemberFront == forward_range); + static_assert( + CanMemberFront == (forward_range && is_reference_v>) ); #if 0 // FIXME + if constexpr (CanMemberFront) { + if (!is_empty) { + assert(r.front() == *begin(expected)); + } + } + static_assert(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + if (!is_empty) { + assert(r.back() == *prev(end(expected))); + } + } + static_assert(!CanMemberBack); if (!is_empty) { if constexpr (random_access_range) { assert(r[0] == *begin(expected)); @@ -246,7 +263,6 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } - // Validate view_interface::front and back assert(r.front() == *begin(expected)); assert(r.back() == *prev(end(expected))); From 15c42b0f8fe5d58705809dc21a41c9a3783bfea2 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 20 Feb 2021 13:16:46 +0100 Subject: [PATCH 32/55] Add join_view::front and join_view::back --- tests/std/tests/P0896R4_views_join/test.cpp | 44 ++++++++------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index f046316f3d6..7814d23c16f 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -237,44 +237,34 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { static_assert(!CanIndex); // Validate view_interface::front and back - static_assert(CanMemberFront == forward_range); - static_assert( - CanMemberFront == (forward_range && is_reference_v>) ); -#if 0 // FIXME - if constexpr (CanMemberFront) { - if (!is_empty) { + static_assert(CanMemberFront == forward_range); + static_assert(CanMemberFront == forward_range); + if (!is_empty) { + if constexpr (CanMemberFront) { assert(r.front() == *begin(expected)); } - } - static_assert(CanMemberBack == (bidirectional_range && common_range) ); - if constexpr (CanMemberBack) { - if (!is_empty) { - assert(r.back() == *prev(end(expected))); + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected)); } } - static_assert(!CanMemberBack); - if (!is_empty) { - if constexpr (random_access_range) { - assert(r[0] == *begin(expected)); - if constexpr (common_range) { - assert(as_const(r)[0] == *begin(expected)); - } + static_assert(CanMemberBack == (bidirectional_range && common_range) ); + // clang-format off + static_assert(CanMemberBack == (bidirectional_range && common_range + && is_reference_v>) ); + // clang-format on + if (!is_empty) { + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); } - assert(r.front() == *begin(expected)); - assert(r.back() == *prev(end(expected))); - - static_assert(CanMemberFront == common_range); - static_assert(CanMemberBack == common_range); - if constexpr (common_range) { - assert(as_const(r).front() == *begin(expected)); + if constexpr (CanMemberBack) { assert(as_const(r).back() == *prev(end(expected))); } } - - // Validate join_view::base() const& +#if 0 // FIXME + // Validate join_view::base() const& static_assert(CanMemberBase == copy_constructible); if constexpr (copy_constructible) { same_as auto b1 = as_const(r).base(); From 38911fae5f955fe8281cf2211a6b62985c1c1363 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 20 Feb 2021 13:37:09 +0100 Subject: [PATCH 33/55] Start testing view_interface::base --- tests/std/tests/P0896R4_views_join/test.cpp | 25 ++++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 7814d23c16f..44d4d165484 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -263,30 +263,33 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { assert(as_const(r).back() == *prev(end(expected))); } } -#if 0 // FIXME - // Validate join_view::base() const& + + // Validate join_view::base() const& static_assert(CanMemberBase == copy_constructible); if constexpr (copy_constructible) { - same_as auto b1 = as_const(r).base(); + 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() == *prev(end(expected))); - if constexpr (common_range) { - assert(*prev(b1.end()) == *begin(expected)); + [[maybe_unused]] same_as auto i1 = *b1.begin(); +#if 0 // FIXME + if (size(i1) != 0) { + assert(*begin(i1) == *begin(expected)); } +#endif // FIXME } } // Validate join_view::base() && (NB: do this last since it leaves r moved-from) - same_as auto b2 = move(r).base(); + same_as auto b2 = move(r).base(); static_assert(noexcept(move(r).base()) == is_nothrow_move_constructible_v); if (!is_empty) { - assert(*b2.begin() == *prev(end(expected))); - if constexpr (common_range) { - assert(*prev(b2.end()) == *begin(expected)); + [[maybe_unused]] same_as auto i2 = *b2.begin(); +#if 0 // FIXME + if (size(i2) != 0) { + assert(*begin(i2) == *begin(expected)); } - } #endif // FIXME + } } return true; } From 50949deb36c4a1dcd12525737da3cc6e8517236a Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 5 Mar 2021 20:29:18 +0100 Subject: [PATCH 34/55] Fix the tests a bit --- stl/inc/ranges | 5 ++--- tests/std/tests/P0896R4_views_join/test.cpp | 14 ++++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index a49929b8f49..13e1e988ed4 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2743,9 +2743,8 @@ namespace ranges { } // clang-format off - _NODISCARD constexpr _InnerIter operator->() const // Per resolution of LWG-3500 - noexcept(is_nothrow_copy_constructible_v<_InnerIter>) /* strengthened */ - requires _Has_arrow<_InnerIter> && copyable<_InnerIter> { + _NODISCARD constexpr _InnerIter operator->() const noexcept(is_nothrow_copy_constructible_v<_InnerIter>) + /* strengthened */ requires _Has_arrow<_InnerIter> && copyable<_InnerIter> { // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 _Check_dereference(); diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 44d4d165484..642afe48d6b 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -266,8 +266,8 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::base() const& static_assert(CanMemberBase == copy_constructible); - if constexpr (copy_constructible) { - same_as auto b1 = as_const(r).base(); + 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) { [[maybe_unused]] same_as auto i1 = *b1.begin(); @@ -280,15 +280,17 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } // Validate join_view::base() && (NB: do this last since it leaves r moved-from) - same_as auto b2 = move(r).base(); - static_assert(noexcept(move(r).base()) == is_nothrow_move_constructible_v); - if (!is_empty) { - [[maybe_unused]] same_as auto i2 = *b2.begin(); + 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) { + [[maybe_unused]] same_as auto i2 = *b2.begin(); #if 0 // FIXME if (size(i2) != 0) { assert(*begin(i2) == *begin(expected)); } #endif // FIXME + } } } return true; From 0a50113b5236f510ecf84fab582c444e13594244 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 5 Mar 2021 21:04:58 +0100 Subject: [PATCH 35/55] Try enable more of the base checks --- tests/std/tests/P0896R4_views_join/test.cpp | 31 +++++++++++---------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 642afe48d6b..856e02f9258 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -267,30 +267,31 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::base() const& static_assert(CanMemberBase == copy_constructible); if constexpr (copy_constructible && forward_range) { - same_as auto b1 = as_const(r).base(); + [[maybe_unused]] same_as auto b1 = as_const(r).base(); static_assert(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); if (!is_empty) { - [[maybe_unused]] same_as auto i1 = *b1.begin(); -#if 0 // FIXME - if (size(i1) != 0) { - assert(*begin(i1) == *begin(expected)); + auto bi1 = b1.begin(); + while (ranges::empty(*bi1)) { + ++bi1; + } + const same_as auto inner_first = *bi1; + assert(*ranges::begin(inner_first) == *begin(expected)); + + if constexpr (bidirectional_range && common_range) { + auto ei1 = prev(b1.end()); + while (ranges::empty(*ei1)) { + --ei1; + } + const same_as auto inner_last = *ei1; + assert(*prev(ranges::end(inner_last)) == *prev(end(expected))); } -#endif // FIXME } } // Validate join_view::base() && (NB: do this last since it leaves r moved-from) if (forward_range) { // intentionally not if constexpr - same_as auto b2 = move(r).base(); + [[maybe_unused]] same_as auto b2 = move(r).base(); static_assert(noexcept(move(r).base()) == is_nothrow_move_constructible_v); - if (!is_empty) { - [[maybe_unused]] same_as auto i2 = *b2.begin(); -#if 0 // FIXME - if (size(i2) != 0) { - assert(*begin(i2) == *begin(expected)); - } -#endif // FIXME - } } } return true; From 9627511b6449d9ec7cec2da0a125026abb580525 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 9 Mar 2021 12:15:50 -0800 Subject: [PATCH 36/55] Don't assume that we can construct a value_type from a reference_type --- tests/std/tests/P0896R4_views_join/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 856e02f9258..d763c107d68 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -274,7 +274,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { while (ranges::empty(*bi1)) { ++bi1; } - const same_as auto inner_first = *bi1; + auto&& inner_first = *bi1; assert(*ranges::begin(inner_first) == *begin(expected)); if constexpr (bidirectional_range && common_range) { @@ -282,7 +282,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { while (ranges::empty(*ei1)) { --ei1; } - const same_as auto inner_last = *ei1; + auto&& inner_last = *ei1; assert(*prev(ranges::end(inner_last)) == *prev(end(expected))); } } From bd5e1192100c6296d215e1dbff7bb45ad14f91f4 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 12 Mar 2021 21:08:54 +0100 Subject: [PATCH 37/55] Finish tests for base() --- tests/std/tests/P0896R4_views_join/test.cpp | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index d763c107d68..f70943dcd76 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -267,7 +267,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::base() const& static_assert(CanMemberBase == copy_constructible); if constexpr (copy_constructible && forward_range) { - [[maybe_unused]] same_as auto b1 = as_const(r).base(); + same_as auto b1 = as_const(r).base(); static_assert(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); if (!is_empty) { auto bi1 = b1.begin(); @@ -290,8 +290,27 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::base() && (NB: do this last since it leaves r moved-from) if (forward_range) { // intentionally not if constexpr - [[maybe_unused]] same_as auto b2 = move(r).base(); + same_as auto b2 = move(r).base(); static_assert(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if constexpr (CanEmpty) { + if (!is_empty) { + auto bi2 = b2.begin(); + while (ranges::empty(*bi2)) { + ++bi2; + } + auto&& inner_first = *bi2; + assert(*ranges::begin(inner_first) == *begin(expected)); + + if constexpr (bidirectional_range && common_range) { + auto ei2 = prev(b2.end()); + while (ranges::empty(*ei2)) { + --ei2; + } + auto&& inner_last = *ei2; + assert(*prev(ranges::end(inner_last)) == *prev(end(expected))); + } + } + } } } return true; From fe322612161dddc638da40f084154d11f670a578 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 12 Mar 2021 21:40:59 +0100 Subject: [PATCH 38/55] Add tests for different types of containers --- tests/std/tests/P0896R4_views_join/test.cpp | 26 ++++++++++----------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index f70943dcd76..5b8ad628de6 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -418,10 +419,11 @@ void test_move_only_views() { int main() { // Validate views + constexpr string_view expected = "Hello World!"sv; + { // ...copyable static constexpr array data = {{{}, "Hello "sv, {}, "World!"sv, {}}}; constexpr span input{data}; - constexpr string_view expected = "Hello World!"sv; static_assert(test_one(input, expected)); test_one(input, expected); } @@ -436,22 +438,18 @@ int main() { test_one(join_me, expected_ints); } #endif // TRANSITION, FIXME -#if 0 // FIXME - { // ... contiguous container - string str{"Hello, World!"}; - constexpr auto expected = "!dlroW ,olleH"sv; - test_one(str, expected); + { // ... fwd container + forward_list lst = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + test_one(lst, expected); } { // ... bidi container - list lst{3, 4, 5}; - static constexpr int joined[] = {5, 4, 3}; - test_one(lst, joined); - - static constexpr int joined_prefix[] = {4, 3}; - assert(ranges::equal( - views::join(ranges::subrange{counted_iterator{lst.begin(), 2}, default_sentinel}), joined_prefix)); + list lst = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + test_one(lst, expected); + } + { // ... random container + vector lst = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + test_one(lst, expected); } -#endif // FIXME #if defined(__clang__) || defined(__EDG__) // FIXME: C1060 "out of heap space" // Get full instantiation coverage From e8dc570036bcc2cf439d001f0c2464799930d034 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 13 Mar 2021 08:25:07 +0100 Subject: [PATCH 39/55] Fix some formatting --- stl/inc/ranges | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 13e1e988ed4..be7a4eaaa00 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2821,9 +2821,11 @@ namespace ranges { return _RANGES iter_move(_It._Inner); } + // clang-format off friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_RANGES iter_swap(_Left._Inner, - _Right._Inner))) requires indirectly_swappable<_InnerIter> { // per LWG-3517 + noexcept(_RANGES iter_swap(_Left._Inner, _Right._Inner))) + requires indirectly_swappable<_InnerIter> { // per LWG-3517 + // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Check_dereference(); _Right._Check_dereference(); From d94ba34065aef427d42eb408dc2687f5331d3805 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 13 Mar 2021 08:33:27 +0100 Subject: [PATCH 40/55] Remove std:: in test --- tests/std/tests/P0896R4_views_join/test.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 5b8ad628de6..7c62a219368 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -170,9 +170,8 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } if constexpr (CanMemberBegin && !CanBegin) { - static_assert(input_iterator().begin())>); - static_assert( - sentinel_for().end()), decltype(std::declval().begin())>); + static_assert(input_iterator().begin())>); + static_assert(sentinel_for().end()), decltype(declval().begin())>); } if constexpr (CanBegin && forward_range) { From 005fed20be4dca612f1838e2111c5eef90fcdcbd Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 13 Mar 2021 08:51:45 +0100 Subject: [PATCH 41/55] Address review comments from STL --- stl/inc/ranges | 10 +++++--- tests/std/tests/P0896R4_views_join/test.cpp | 28 ++++++++++----------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index be7a4eaaa00..3f08792fc50 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2609,7 +2609,7 @@ namespace ranges { class _Join_view_base<_Vw> : public view_interface> {}; template - requires view<_Vw>&& input_range> + requires view<_Vw> && input_range> && (is_reference_v> || view>) class join_view : public _Join_view_base<_Vw> { // clang-format on @@ -2625,7 +2625,7 @@ namespace ranges { // clang-format off template - requires _Has_member_iterator_category<_InnerTraits> && _Has_member_iterator_category<_OuterTraits> + requires _Has_member_iterator_category<_OuterTraits> && _Has_member_iterator_category<_InnerTraits> struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_glvalue> { using iterator_category = conditional_t<_Deref_is_glvalue @@ -2672,11 +2672,13 @@ namespace ranges { (void) this; // Suppress Clang "unused capture" warning return _Range; } else { - return (_Parent->_Inner = views::all(_STD move(_Range))); + _Parent->_Inner = views::all(_STD move(_Range)); + return _Parent->_Inner; } }; - for (; _Outer != _RANGES end(_Parent->_Range); ++_Outer) { + const auto _Last = _RANGES end(_Parent->_Range); + for (; _Outer != _Last; ++_Outer) { auto& _Tmp = _Update_inner(*_Outer); _Inner = _RANGES begin(_Tmp); if (_Inner != _RANGES end(_Tmp)) { diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 7c62a219368..a6df20a0b7a 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -400,20 +400,20 @@ void test_move_only_views() { return array{mo_inner{intervals[0]}, mo_inner{intervals[1]}, mo_inner{intervals[2]}, mo_inner{intervals[3]}}; }; - auto data = gen(); - test_one(move_only_view{data}, expected_ints); + auto input = gen(); + test_one(move_only_view{input}, expected_ints); - data = gen(); - test_one(move_only_view{data}, expected_ints); + input = gen(); + test_one(move_only_view{input}, expected_ints); - data = gen(); - test_one(move_only_view{data}, expected_ints); + input = gen(); + test_one(move_only_view{input}, expected_ints); - data = gen(); - test_one(move_only_view{data}, expected_ints); + input = gen(); + test_one(move_only_view{input}, expected_ints); - data = gen(); - test_one(move_only_view{data}, expected_ints); + input = gen(); + test_one(move_only_view{input}, expected_ints); } int main() { @@ -421,10 +421,10 @@ int main() { constexpr string_view expected = "Hello World!"sv; { // ...copyable - static constexpr array data = {{{}, "Hello "sv, {}, "World!"sv, {}}}; - constexpr span input{data}; - static_assert(test_one(input, expected)); - test_one(input, expected); + static constexpr array input = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + constexpr span sp{input}; + static_assert(test_one(sp, expected)); + test_one(sp, expected); } // ... move-only test_move_only_views(); From 198e45640f466b730424263acee88477fbac6238 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 13 Mar 2021 08:54:45 +0100 Subject: [PATCH 42/55] Remove incorect noexcept specifications --- stl/inc/ranges | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 3f08792fc50..d5d518f4cc1 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2895,23 +2895,20 @@ namespace ranges { return _STD move(_Range); } - _NODISCARD constexpr auto begin() noexcept( - noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { + _NODISCARD constexpr auto begin() { constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v>; return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; } // clang-format off - _NODISCARD constexpr _Iterator begin() const noexcept(noexcept( - _RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ + _NODISCARD constexpr _Iterator begin() const requires input_range && is_reference_v> { // clang-format on return _Iterator{*this, _RANGES begin(_Range)}; } // clang-format off - _NODISCARD constexpr auto end() noexcept(noexcept( - _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { + _NODISCARD constexpr auto end() { if constexpr (forward_range<_Vw> && is_reference_v> && forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { // clang-format on @@ -2922,8 +2919,7 @@ namespace ranges { } // clang-format off - _NODISCARD constexpr auto end() const noexcept(noexcept( - _RANGES end(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ + _NODISCARD constexpr auto end() const requires input_range && is_reference_v> { if constexpr (forward_range && is_reference_v> && forward_range> && common_range From 3f264c2db26d282ba03d239229d412f08c816577 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 15 Mar 2021 16:24:46 -0700 Subject: [PATCH 43/55] Remove redundant begin/end checks (no caching) --- tests/std/tests/P0896R4_views_join/test.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index a6df20a0b7a..c9459bf5200 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -155,7 +155,6 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { static_assert(CanMemberBegin == (input_range && is_reference_v>) ); if constexpr (forward_range) { const iterator_t i = r.begin(); - assert(r.begin() == i); if (!is_empty) { assert(*i == *begin(expected)); } @@ -163,7 +162,6 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { if constexpr (copyable) { auto r2 = r; const same_as> auto i2 = r2.begin(); - assert(r2.begin() == i2); if (!is_empty) { assert(*i2 == *i); } @@ -176,7 +174,6 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { if constexpr (CanBegin && forward_range) { const iterator_t ci = as_const(r).begin(); - assert(as_const(r).begin() == ci); if (!is_empty) { assert(*ci == *i); } @@ -184,7 +181,6 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { if constexpr (copyable) { const auto r2 = r; const same_as> auto ci2 = r2.begin(); - assert(r2.begin() == ci2); if (!is_empty) { assert(*ci2 == *i); } From 49a06c0c731fe64fad58b2c6831af1eea77810f0 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 15 Mar 2021 16:25:36 -0700 Subject: [PATCH 44/55] Add trailing empty interval --- tests/std/tests/P0896R4_views_join/test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index c9459bf5200..172e21cb306 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -373,13 +373,15 @@ constexpr span intervals[] = { {expected_ints + 3, expected_ints + 7}, {expected_ints + 7, expected_ints + 7}, {expected_ints + 7, expected_ints + 10}, + {expected_ints + 10, expected_ints + 10}, }; struct instantiator { template static constexpr void call() { - static_assert(ranges::size(intervals) == 4); - array inner_ranges = {Inner{intervals[0]}, Inner{intervals[1]}, Inner{intervals[2]}, Inner{intervals[3]}}; + static_assert(ranges::size(intervals) == 5); + Inner inner_ranges[] = { + Inner{intervals[0]}, Inner{intervals[1]}, Inner{intervals[2]}, Inner{intervals[3]}, Inner{intervals[4]}}; test_one(Outer{inner_ranges}, expected_ints); } }; From e8179e6f73d12016db745ba0ea1526d9dc205dd0 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 15 Mar 2021 16:39:31 -0700 Subject: [PATCH 45/55] Avoid unreduced MSVC bug --- stl/inc/ranges | 6 ++++-- tests/std/tests/P0896R4_views_join/test.cpp | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index d5d518f4cc1..b6ff20b10c2 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2785,10 +2785,12 @@ namespace ranges { && common_range<_SubRng> { // clang-format on if (_Outer == _RANGES end(_Parent->_Range)) { - _Inner = _RANGES end(*--_Outer); + --_Outer; + _Inner = _RANGES end(*_Outer); } while (_Inner == _RANGES begin(*_Outer)) { - _Inner = _RANGES end(*--_Outer); + --_Outer; + _Inner = _RANGES end(*_Outer); } --_Inner; return *this; diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 172e21cb306..1b19d3acc25 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -428,13 +428,13 @@ int main() { test_move_only_views(); // Validate non-views -#if defined(__clang__) || defined(__EDG__) // TRANSITION, FIXME { // ... C array static constexpr int join_me[5][2] = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}}; +#if defined(__clang__) || defined(__EDG__) // TRANSITION, Unfiled MSVC bug static_assert(test_one(join_me, expected_ints)); +#endif // TRANSITION, Unfiled MSVC bug test_one(join_me, expected_ints); } -#endif // TRANSITION, FIXME { // ... fwd container forward_list lst = {{{}, "Hello "sv, {}, "World!"sv, {}}}; test_one(lst, expected); From 50c42935bd2d7febce29053bdf4b11b945f6059a Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 15 Mar 2021 19:47:14 -0700 Subject: [PATCH 46/55] Remove extraneous copypasta braces --- tests/std/tests/P0896R4_views_join/test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 1b19d3acc25..c6305fc0d91 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -436,15 +436,15 @@ int main() { test_one(join_me, expected_ints); } { // ... fwd container - forward_list lst = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + forward_list lst = {{}, "Hello "sv, {}, "World!"sv, {}}; test_one(lst, expected); } { // ... bidi container - list lst = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + list lst = {{}, "Hello "sv, {}, "World!"sv, {}}; test_one(lst, expected); } { // ... random container - vector lst = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + vector lst = {{}, "Hello "sv, {}, "World!"sv, {}}; test_one(lst, expected); } From ac546dcaed1ce61f97a53e2aa291a008f1ce211a Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 16 Mar 2021 11:18:49 -0700 Subject: [PATCH 47/55] Fix Standard defect in iterator_{category,concept} --- stl/inc/ranges | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index b6ff20b10c2..163cc3a5b31 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2620,15 +2620,16 @@ namespace ranges { template class _Sentinel; - template // TRANSITION, LWG-3289 + template // TRANSITION, LWG-3289 struct _Category_base {}; // clang-format off - template + template requires _Has_member_iterator_category<_OuterTraits> && _Has_member_iterator_category<_InnerTraits> - struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_glvalue> { + struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_glvalue, _Inner_common> { using iterator_category = - conditional_t<_Deref_is_glvalue + conditional_t<_Deref_is_glvalue && _Inner_common // per LWG issue unnumbered as of 2021-03-16 && derived_from && derived_from, bidirectional_iterator_tag, @@ -2646,7 +2647,8 @@ namespace ranges { template class _Iterator : public _Category_base>>, iterator_traits>>>, - is_reference_v>>> { + is_reference_v>>, + common_range>>> { private: template friend class _Iterator; @@ -2709,11 +2711,13 @@ namespace ranges { #endif // _ITERATOR_DEBUG_LEVEL != 0 public: - using iterator_concept = - conditional_t<_Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_SubRng>, - bidirectional_iterator_tag, - conditional_t<_Deref_is_glvalue && forward_range<_Base> && forward_range<_SubRng>, - forward_iterator_tag, input_iterator_tag>>; + // clang-format off + // Per LWG issue unnumbered as of 2021-03-16 + using iterator_concept = conditional_t<_Deref_is_glvalue && bidirectional_range<_Base> + && bidirectional_range<_SubRng> && common_range<_SubRng>, bidirectional_iterator_tag, + conditional_t<_Deref_is_glvalue && forward_range<_Base> && forward_range<_SubRng>, + forward_iterator_tag, input_iterator_tag>>; + // clang-format on using value_type = range_value_t<_SubRng>; using difference_type = common_type_t, range_difference_t<_SubRng>>; @@ -2865,11 +2869,12 @@ namespace ranges { public: _Sentinel() = default; - // clang-format off - constexpr explicit _Sentinel(_Parent_t& _Parent) noexcept(noexcept(_RANGES end(_Parent._Range)) + constexpr explicit _Sentinel(_Parent_t& _Parent) noexcept( + noexcept(_RANGES end(_Parent._Range)) && is_nothrow_move_constructible_v>) // strengthened : _Last(_RANGES end(_Parent._Range)) {} + // clang-format off constexpr _Sentinel(_Sentinel _Se) noexcept(is_nothrow_constructible_v, sentinel_t<_Vw>>) // strengthened requires _Const && convertible_to, sentinel_t<_Base>> From 94692920c1c865d7b315f3ffb63c9c40a32fcc45 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 16 Mar 2021 11:19:16 -0700 Subject: [PATCH 48/55] Hack test machinery for prvalue reference types --- tests/std/include/range_algorithm_support.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 901f64822b6..5bb57d7c8c4 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -87,7 +87,7 @@ namespace test { enum class CanDifference : bool { no, yes }; enum class CanCompare : bool { no, yes }; - enum class ProxyRef : bool { no, yes }; + enum class ProxyRef { no, yes, prvalue }; enum class IsWrapped : bool { no, yes }; template @@ -341,14 +341,14 @@ namespace test { // Interact with the STL's iterator unwrapping machinery? IsWrapped Wrapped = IsWrapped::yes> requires (to_bool(Eq) || !derived_from) - && (!to_bool(Proxy) || !derived_from) + && (Proxy == ProxyRef::no || !derived_from) class iterator { Element* ptr_; template static constexpr bool at_least = derived_from; - using ReferenceType = conditional_t, Element&>; + using ReferenceType = conditional_t, conditional_t, Element&>>; struct post_increment_proxy { Element* ptr_; @@ -590,7 +590,7 @@ template > { using iterator_concept = Category; using iterator_category = conditional_t, // - conditional_t(Proxy), input_iterator_tag, Category>, // + conditional_t, // conditional_t(Eq), Category, void>>; // TRANSITION, LWG-3289 using value_type = remove_cv_t; using difference_type = ptrdiff_t; @@ -670,11 +670,11 @@ namespace test { template class range_base { public: - range_base() = default; + constexpr range_base() = default; constexpr explicit range_base(span elements) noexcept : elements_{elements} {} - range_base(const range_base&) = default; - range_base& operator=(const range_base&) = default; + constexpr range_base(const range_base&) = default; + constexpr range_base& operator=(const range_base&) = default; constexpr range_base(range_base&& that) noexcept : elements_{that.elements_}, moved_from_{that.moved_from_} { @@ -720,7 +720,7 @@ namespace test { Copyability Copy = IsView == CanView::yes ? Copyability::move_only : Copyability::immobile> requires (!to_bool(IsCommon) || to_bool(Eq)) && (to_bool(Eq) || !derived_from) - && (!to_bool(Proxy) || !derived_from) + && (Proxy == ProxyRef::no || !derived_from) && (!to_bool(IsView) || Copy != Copyability::immobile) class range : public detail::range_base { private: From b28eeceb24967c23a12fa279667a0df1f9404a81 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 16 Mar 2021 11:19:57 -0700 Subject: [PATCH 49/55] Complete test overhaul --- tests/std/tests/P0896R4_views_join/test.cpp | 222 ++++++++++++-------- 1 file changed, 135 insertions(+), 87 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index c6305fc0d91..d8bbdcd020c 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -23,16 +23,16 @@ concept CanViewJoin = requires(Rng&& r) { template constexpr bool test_one(Outer&& rng, Expected&& expected) { - using ranges::begin, ranges::bidirectional_range, ranges::common_range, ranges::enable_borrowed_range, ranges::end, - ranges::forward_range, ranges::input_range, ranges::iterator_t, ranges::join_view, ranges::random_access_range, - ranges::range_value_t, ranges::range_reference_t; + using ranges::join_view, ranges::begin, ranges::end, ranges::next, ranges::prev, ranges::input_range, + ranges::forward_range, ranges::bidirectional_range, ranges::common_range, ranges::borrowed_range, + ranges::iterator_t, ranges::range_value_t, ranges::range_reference_t; using Inner = range_value_t; constexpr bool deref_is_glvalue = is_reference_v>; // clang-format off constexpr bool can_test = ranges::viewable_range - && ranges::input_range> + && input_range> && (deref_is_glvalue || ranges::view); // clang-format on @@ -41,8 +41,10 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { using R = join_view; static_assert(ranges::view); static_assert(input_range == input_range); - static_assert(forward_range == (forward_range && forward_range) ); - static_assert(bidirectional_range == (bidirectional_range && bidirectional_range) ); + static_assert(forward_range == (deref_is_glvalue && forward_range && forward_range) ); + static_assert( + bidirectional_range< + R> == (deref_is_glvalue && bidirectional_range && bidirectional_range && common_range) ); static_assert(!ranges::random_access_range); static_assert(!ranges::contiguous_range); @@ -83,8 +85,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } // ... with rvalue argument - static_assert( - CanViewJoin> == is_view || enable_borrowed_range>); + static_assert(CanViewJoin> == (is_view || borrowed_range>) ); if constexpr (is_view) { constexpr bool is_noexcept = is_nothrow_move_constructible_v; static_assert(same_as); @@ -92,7 +93,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { static_assert(same_as); static_assert(noexcept(move(rng) | views::join) == is_noexcept); - } else if constexpr (enable_borrowed_range) { + } else if constexpr (borrowed_range>) { using S = decltype(ranges::subrange{move(rng)}); using RS = join_view; constexpr bool is_noexcept = noexcept(S{move(rng)}); @@ -106,7 +107,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // ... with const rvalue argument static_assert(CanViewJoin> == (is_view && copyable) - || (!is_view && enable_borrowed_range>) ); + || (!is_view && borrowed_range>) ); if constexpr (is_view && copyable) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v; @@ -115,7 +116,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { static_assert(same_as); static_assert(noexcept(move(as_const(rng)) | views::join) == is_noexcept); - } else if constexpr (!is_view && enable_borrowed_range>) { + } else if constexpr (!is_view && borrowed_range>) { using S = decltype(ranges::subrange{as_const(rng)}); using RS = join_view; constexpr bool is_noexcept = noexcept(S{as_const(rng)}); @@ -202,23 +203,21 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { const ranges::sentinel_t s = r.end(); if constexpr (bidirectional_range && common_range) { assert(*prev(s) == *prev(end(expected))); - } - if constexpr (bidirectional_range && common_range && copyable) { - auto r2 = r; - assert(*prev(r2.end()) == *prev(end(expected))); + if constexpr (copyable) { + auto r2 = r; + assert(*prev(r2.end()) == *prev(end(expected))); + } } if constexpr (CanMemberEnd) { const ranges::sentinel_t cs = as_const(r).end(); - if constexpr (bidirectional_range) { + if constexpr (bidirectional_range && common_range) { assert(*prev(cs) == *prev(end(expected))); - } - if constexpr (copyable) { - const auto r2 = r; - const ranges::sentinel_t cs2 = r2.end(); - if constexpr (bidirectional_range) { + if constexpr (copyable) { + const auto r2 = r; + const ranges::sentinel_t cs2 = r2.end(); assert(*prev(cs2) == *prev(end(expected))); } } @@ -271,15 +270,15 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { ++bi1; } auto&& inner_first = *bi1; - assert(*ranges::begin(inner_first) == *begin(expected)); + assert(*begin(inner_first) == *begin(expected)); - if constexpr (bidirectional_range && common_range) { + if constexpr (bidirectional_range && common_range) { auto ei1 = prev(b1.end()); while (ranges::empty(*ei1)) { --ei1; } auto&& inner_last = *ei1; - assert(*prev(ranges::end(inner_last)) == *prev(end(expected))); + assert(*prev(end(inner_last)) == *prev(end(expected))); } } } @@ -295,15 +294,15 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { ++bi2; } auto&& inner_first = *bi2; - assert(*ranges::begin(inner_first) == *begin(expected)); + assert(*begin(inner_first) == *begin(expected)); - if constexpr (bidirectional_range && common_range) { + if constexpr (bidirectional_range && common_range) { auto ei2 = prev(b2.end()); while (ranges::empty(*ei2)) { --ei2; } auto&& inner_last = *ei2; - assert(*prev(ranges::end(inner_last)) == *prev(end(expected))); + assert(*prev(end(inner_last)) == *prev(end(expected))); } } } @@ -312,61 +311,6 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { return true; } -template -struct with_dependent_input_ranges { - template - static constexpr void call() { - using namespace test; - using test::range; - - // For all ranges, IsCommon implies Eq. - // For single-pass ranges, Eq is uninteresting without IsCommon (there's only one valid iterator - // value at a time, and no reason to compare it with itself for equality). - Continuation::template call>(); - Continuation::template call>(); - Continuation::template call>(); - Continuation::template call>(); - - Continuation::template call>(); - Continuation::template call>(); - Continuation::template call>(); - Continuation::template call>(); - - Continuation::template call>(); - Continuation::template call>(); - Continuation::template call>(); - Continuation::template call>(); - - Continuation::template call>(); - Continuation::template call>(); - Continuation::template call>(); - Continuation::template call>(); - - with_forward_ranges::template call(); - } -}; - -template -constexpr void test_nested_inout() { - with_input_or_output_ranges, Element>::call(); -} - constexpr int expected_ints[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; constexpr span intervals[] = { {expected_ints + 0, expected_ints + 3}, @@ -386,6 +330,111 @@ struct instantiator { } }; +enum class RefOrView { reference, view }; + +template > +using inner_test_range = test::range || IsCommon == test::Common::yes}, + test::ProxyRef::no, IsView, test::Copyability::copyable>; + +template > +using outer_test_range = test::range || IsCommon == test::Common::yes}, + (RV == RefOrView::view ? test::ProxyRef::prvalue : test::ProxyRef::no), test::CanView::yes, + test::Copyability::copyable>; + +constexpr bool instantiation_test() { + // The adaptor is sensitive to: + // * inner and outer range common category (input, forward, bidi) + // * outer range's reference type referenceness vs. value type viewness + // * if the inner range models common_range + // * if the outer range models common_range + // * if both inner and outer iterators are equality_comparable (the defaults for input-non-common and forward + // suffice to get coverage here) + // * if the inner range has -> (Ditto defaults) + using test::CanView, test::Common; + + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::reference, + Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::reference, + Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::reference, + Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::yes>>(); + + return true; +} + using mo_inner = test::range; @@ -448,9 +497,8 @@ int main() { test_one(lst, expected); } -#if defined(__clang__) || defined(__EDG__) // FIXME: C1060 "out of heap space" - // Get full instantiation coverage - static_assert((test_nested_inout(), true)); - test_nested_inout(); -#endif // FIXME +#if defined(__clang__) || defined(__EDG__) // TRANSITION, Unfiled MSVC bug + STATIC_ASSERT(instantiation_test()); +#endif // TRANSITION, Unfiled MSVC bug + instantiation_test(); } From 39ecceb0ac2f6d214d72119f5565cb316dacf073 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 16 Mar 2021 11:42:44 -0700 Subject: [PATCH 50/55] a few fixes: * wrap overlong lines * avoid lambda in `_Iterator::_Satisfy` from throughput paranoia * the MSVC bug was pre-existing --- stl/inc/ranges | 19 +++++++++---------- tests/std/include/range_algorithm_support.hpp | 3 ++- tests/std/tests/P0896R4_views_join/test.cpp | 15 ++++++++------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 163cc3a5b31..7cf4e683a1f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2668,17 +2668,16 @@ namespace ranges { /* [[no_unique_address]] */ _InnerIter _Inner{}; _Parent_t* _Parent{}; - constexpr void _Satisfy() { - auto _Update_inner = [this](_SubRng _Range) -> auto& { - if constexpr (_Deref_is_glvalue) { - (void) this; // Suppress Clang "unused capture" warning - return _Range; - } else { - _Parent->_Inner = views::all(_STD move(_Range)); - return _Parent->_Inner; - } - }; + constexpr auto& _Update_inner(_SubRng _Range) { + if constexpr (_Deref_is_glvalue) { + return _Range; + } else { + _Parent->_Inner = views::all(_STD move(_Range)); + return _Parent->_Inner; + } + } + constexpr void _Satisfy() { const auto _Last = _RANGES end(_Parent->_Range); for (; _Outer != _Last; ++_Outer) { auto& _Tmp = _Update_inner(*_Outer); diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 5bb57d7c8c4..2731ec53fb3 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -348,7 +348,8 @@ namespace test { template static constexpr bool at_least = derived_from; - using ReferenceType = conditional_t, conditional_t, Element&>>; + using ReferenceType = conditional_t, + conditional_t, Element&>>; struct post_increment_proxy { Element* ptr_; diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index d8bbdcd020c..4863415baea 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -42,9 +42,10 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { static_assert(ranges::view); static_assert(input_range == input_range); static_assert(forward_range == (deref_is_glvalue && forward_range && forward_range) ); - static_assert( - bidirectional_range< - R> == (deref_is_glvalue && bidirectional_range && bidirectional_range && common_range) ); + // clang-format off + static_assert(bidirectional_range == + (deref_is_glvalue && bidirectional_range && bidirectional_range && common_range)); + // clang-format on static_assert(!ranges::random_access_range); static_assert(!ranges::contiguous_range); @@ -479,9 +480,9 @@ int main() { // Validate non-views { // ... C array static constexpr int join_me[5][2] = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}}; -#if defined(__clang__) || defined(__EDG__) // TRANSITION, Unfiled MSVC bug +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-934264 static_assert(test_one(join_me, expected_ints)); -#endif // TRANSITION, Unfiled MSVC bug +#endif // TRANSITION, VSO-934264 test_one(join_me, expected_ints); } { // ... fwd container @@ -497,8 +498,8 @@ int main() { test_one(lst, expected); } -#if defined(__clang__) || defined(__EDG__) // TRANSITION, Unfiled MSVC bug +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-934264 STATIC_ASSERT(instantiation_test()); -#endif // TRANSITION, Unfiled MSVC bug +#endif // TRANSITION, VSO-934264 instantiation_test(); } From 072d985871f93d177f69582782726928745bc8d9 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 16 Mar 2021 13:46:43 -0700 Subject: [PATCH 51/55] Add test case with example from LWG-3474 --- tests/std/tests/P0896R4_views_join/test.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 4863415baea..b9b4eecd23d 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include using namespace std; @@ -498,6 +499,13 @@ int main() { test_one(lst, expected); } + { // From example in LWG-3474 + vector>> nested_vectors = {{{1, 2, 3}, {4, 5}, {6}}, {{7}, {8, 9}, {10, 11, 12}}, {{13}}}; + auto joined = nested_vectors | views::join | views::join; + static constexpr int result[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + assert(ranges::equal(joined, result)); + } + #if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-934264 STATIC_ASSERT(instantiation_test()); #endif // TRANSITION, VSO-934264 From ba8ccd924221bce90f44839e2525dd12ea271d79 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 16 Mar 2021 13:58:49 -0700 Subject: [PATCH 52/55] Drive-by replace `ranges::` with `_RANGES ` --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 7cf4e683a1f..d6ec22e17c6 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1633,7 +1633,7 @@ namespace ranges { } friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) _NOEXCEPT_IDL0(noexcept( - ranges::iter_swap(_Left._Current, _Right._Current))) requires indirectly_swappable> { + _RANGES iter_swap(_Left._Current, _Right._Current))) requires indirectly_swappable> { #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Check_dereference(); _Right._Check_dereference(); From 6b2334ba0c6c8972e252c74e798d0edc09a83e1b Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 16 Mar 2021 15:58:10 -0700 Subject: [PATCH 53/55] All outstanding review comments --- stl/inc/ranges | 65 +++++++++++---------- tests/std/tests/P0896R4_views_join/test.cpp | 16 +++-- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index d6ec22e17c6..e626e2a3b88 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2614,7 +2614,8 @@ namespace ranges { class join_view : public _Join_view_base<_Vw> { // clang-format on private: - using _InnerRng = range_reference_t<_Vw>; + template + using _InnerRng = range_reference_t<_Maybe_const<_Const, _Vw>>; /* [[no_unique_address]] */ _Vw _Range{}; template @@ -2646,9 +2647,8 @@ namespace ranges { template class _Iterator : public _Category_base>>, - iterator_traits>>>, - is_reference_v>>, - common_range>>> { + iterator_traits>>, is_reference_v<_InnerRng<_Const>>, + common_range<_InnerRng<_Const>>> { private: template friend class _Iterator; @@ -2657,18 +2657,17 @@ namespace ranges { using _Parent_t = _Maybe_const<_Const, join_view>; using _Base = _Maybe_const<_Const, _Vw>; - using _SubRng = range_reference_t<_Base>; using _OuterIter = iterator_t<_Base>; - using _InnerIter = iterator_t<_SubRng>; + using _InnerIter = iterator_t<_InnerRng<_Const>>; // True if and only if the expression *i, where i is an iterator from the outer range, is a glvalue: - static constexpr bool _Deref_is_glvalue = is_reference_v<_SubRng>; + static constexpr bool _Deref_is_glvalue = is_reference_v<_InnerRng<_Const>>; /* [[no_unique_address]] */ _OuterIter _Outer{}; /* [[no_unique_address]] */ _InnerIter _Inner{}; _Parent_t* _Parent{}; - constexpr auto& _Update_inner(_SubRng _Range) { + constexpr auto& _Update_inner(_InnerRng<_Const> _Range) { if constexpr (_Deref_is_glvalue) { return _Range; } else { @@ -2694,14 +2693,14 @@ namespace ranges { #if _ITERATOR_DEBUG_LEVEL != 0 constexpr void _Check_dereference() const noexcept { _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator"); - _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference end join_view iterator"); - sentinel_t<_InnerRng> _Last; + _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference join_view end iterator"); + sentinel_t<_InnerRng> _Last; if constexpr (_Deref_is_glvalue) { _Last = _RANGES end(*_Outer); } else { _Last = _RANGES end(_Parent->_Inner); } - _STL_VERIFY(_Inner != _Last, "cannot dereference end join_view iterator"); + _STL_VERIFY(_Inner != _Last, "cannot dereference join_view end iterator"); } constexpr void _Same_range(const _Iterator& _Right) const noexcept { @@ -2712,13 +2711,14 @@ namespace ranges { public: // clang-format off // Per LWG issue unnumbered as of 2021-03-16 - using iterator_concept = conditional_t<_Deref_is_glvalue && bidirectional_range<_Base> - && bidirectional_range<_SubRng> && common_range<_SubRng>, bidirectional_iterator_tag, - conditional_t<_Deref_is_glvalue && forward_range<_Base> && forward_range<_SubRng>, + using iterator_concept = conditional_t<_Deref_is_glvalue + && bidirectional_range<_Base> && bidirectional_range<_InnerRng<_Const>> + && common_range<_InnerRng<_Const>>, bidirectional_iterator_tag, + conditional_t<_Deref_is_glvalue && forward_range<_Base> && forward_range<_InnerRng<_Const>>, forward_iterator_tag, input_iterator_tag>>; // clang-format on - using value_type = range_value_t<_SubRng>; - using difference_type = common_type_t, range_difference_t<_SubRng>>; + using value_type = range_value_t<_InnerRng<_Const>>; + using difference_type = common_type_t, range_difference_t<_InnerRng<_Const>>>; _Iterator() = default; @@ -2736,7 +2736,7 @@ namespace ranges { // clang-format off constexpr _Iterator(_Iterator _It) requires _Const && convertible_to, _OuterIter> - && convertible_to, _InnerIter> + && convertible_to>, _InnerIter> : _Outer{_STD move(_It._Outer)}, _Inner{_STD move(_It._Inner)}, _Parent{_It._Parent} {} // clang-format on @@ -2773,7 +2773,7 @@ namespace ranges { } constexpr decltype(auto) operator++(int) { - if constexpr (_Deref_is_glvalue && forward_range<_Base> && forward_range<_SubRng>) { + if constexpr (_Deref_is_glvalue && forward_range<_Base> && forward_range<_InnerRng<_Const>>) { auto _Tmp = *this; ++*this; return _Tmp; @@ -2784,8 +2784,8 @@ namespace ranges { // clang-format off constexpr _Iterator& operator--() - requires _Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_SubRng> - && common_range<_SubRng> { + requires _Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_InnerRng<_Const>> + && common_range<_InnerRng<_Const>> { // clang-format on if (_Outer == _RANGES end(_Parent->_Range)) { --_Outer; @@ -2801,8 +2801,8 @@ namespace ranges { // clang-format off constexpr _Iterator operator--(int) - requires _Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_SubRng> - && common_range<_SubRng> { + requires _Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_InnerRng<_Const>> + && common_range<_InnerRng<_Const>> { // clang-format on auto _Tmp = *this; --*this; @@ -2811,7 +2811,8 @@ namespace ranges { // clang-format off _NODISCARD friend constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner)) /* strengthened */ + noexcept(_Implicitly_convert_to(_Left._Outer == _Right._Outer && + _Left._Inner == _Right._Inner))) /* strengthened */ requires _Deref_is_glvalue && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 @@ -2861,7 +2862,7 @@ namespace ranges { template requires sentinel_for, _Maybe_const_iter<_OtherConst>> _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const noexcept( - noexcept(_It._Outer == _Last)) { + noexcept(_Implicitly_convert_to(_It._Outer == _Last))) { // clang-format on return _It._Outer == _Last; } @@ -2902,21 +2903,21 @@ namespace ranges { } _NODISCARD constexpr auto begin() { - constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v>; + constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng>; return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; } // clang-format off _NODISCARD constexpr _Iterator begin() const - requires input_range && is_reference_v> { + requires input_range && is_reference_v<_InnerRng> { // clang-format on return _Iterator{*this, _RANGES begin(_Range)}; } // clang-format off _NODISCARD constexpr auto end() { - if constexpr (forward_range<_Vw> && is_reference_v> - && forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { + if constexpr (forward_range<_Vw> && is_reference_v<_InnerRng> + && forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { // clang-format on return _Iterator<_Simple_view<_Vw>>{*this, _RANGES end(_Range)}; } else { @@ -2926,10 +2927,10 @@ namespace ranges { // clang-format off _NODISCARD constexpr auto end() const - requires input_range && is_reference_v> { - if constexpr (forward_range && is_reference_v> - && forward_range> && common_range - && common_range>) { + requires input_range && is_reference_v<_InnerRng> { + if constexpr (forward_range && is_reference_v<_InnerRng> + && forward_range<_InnerRng> && common_range + && common_range<_InnerRng>) { // clang-format on return _Iterator{*this, _RANGES end(_Range)}; } else { diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index b9b4eecd23d..cd989531f13 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -156,7 +156,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::begin static_assert(CanMemberBegin); static_assert(CanMemberBegin == (input_range && is_reference_v>) ); - if constexpr (forward_range) { + if (forward_range) { const iterator_t i = r.begin(); if (!is_empty) { assert(*i == *begin(expected)); @@ -170,12 +170,8 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } - if constexpr (CanMemberBegin && !CanBegin) { - static_assert(input_iterator().begin())>); - static_assert(sentinel_for().end()), decltype(declval().begin())>); - } - - if constexpr (CanBegin && forward_range) { + static_assert(CanMemberBegin == CanBegin); + if constexpr (CanMemberBegin) { const iterator_t ci = as_const(r).begin(); if (!is_empty) { assert(*ci == *i); @@ -183,7 +179,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { if constexpr (copyable) { const auto r2 = r; - const same_as> auto ci2 = r2.begin(); + const same_as> auto ci2 = r2.begin(); if (!is_empty) { assert(*ci2 == *i); } @@ -201,8 +197,8 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { && common_range && forward_range> && common_range>) ); // clang-format on + const ranges::sentinel_t s = r.end(); if (!is_empty) { - const ranges::sentinel_t s = r.end(); if constexpr (bidirectional_range && common_range) { assert(*prev(s) == *prev(end(expected))); @@ -212,6 +208,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } + static_assert(CanMemberEnd == CanEnd); if constexpr (CanMemberEnd) { const ranges::sentinel_t cs = as_const(r).end(); if constexpr (bidirectional_range && common_range) { @@ -225,6 +222,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } } + // Validate view_interface::data static_assert(!CanData); static_assert(!CanData); From 3bc8db8cdbe450b567057ee9842ee62d4b93f5b3 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 16 Mar 2021 19:18:30 -0700 Subject: [PATCH 54/55] Apply suggestions from code review STL's mechanical fixes. (I certainly won't miss working in `// clang-format off` zones when clang-format learns concepts and requires-clauses.) Co-authored-by: Stephan T. Lavavej --- stl/inc/ranges | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index e626e2a3b88..7381bd948e6 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2710,7 +2710,7 @@ namespace ranges { public: // clang-format off - // Per LWG issue unnumbered as of 2021-03-16 + // Per LWG issue unnumbered as of 2021-03-16 using iterator_concept = conditional_t<_Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_InnerRng<_Const>> && common_range<_InnerRng<_Const>>, bidirectional_iterator_tag, @@ -2811,8 +2811,8 @@ namespace ranges { // clang-format off _NODISCARD friend constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Outer == _Right._Outer && - _Left._Inner == _Right._Inner))) /* strengthened */ + noexcept(_Implicitly_convert_to(_Left._Outer == _Right._Outer + && _Left._Inner == _Right._Inner))) /* strengthened */ requires _Deref_is_glvalue && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 From 78d1ac946882c8fd166700bbd3e33d48a1c6ebcc Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 16 Mar 2021 19:20:52 -0700 Subject: [PATCH 55/55] Update tests/std/include/range_algorithm_support.hpp STL's non-mechanical suggestion. --- tests/std/include/range_algorithm_support.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 2731ec53fb3..3dc819bee4c 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -591,7 +591,7 @@ template > { using iterator_concept = Category; using iterator_category = conditional_t, // - conditional_t, // + conditional_t, // conditional_t(Eq), Category, void>>; // TRANSITION, LWG-3289 using value_type = remove_cv_t; using difference_type = ptrdiff_t;