From c87ce0f97d259ac04d0f6f06eeae7009e5178255 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 13 Jul 2021 17:31:54 +0200 Subject: [PATCH 01/13] Implement rest of P2259 Fixes #1684 --- stl/inc/ranges | 102 +++++++++--------- stl/inc/xutility | 22 ++-- stl/inc/yvals_core.h | 1 - tests/std/include/range_algorithm_support.hpp | 2 +- .../test.cpp | 2 +- .../test.compile.pass.cpp | 2 +- .../tests/P0896R4_views_transform/test.cpp | 12 ++- 7 files changed, 77 insertions(+), 66 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index da54c28b0bc..e0fab9a66b9 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -744,18 +744,28 @@ namespace ranges { { __j - __j } -> convertible_to<_Iota_diff_t<_Ty>>; }; + template + struct _Ioterator_category_base {}; + + template + struct _Ioterator_category_base<_Wi> { + using iterator_category = input_iterator_tag; + }; + template requires copyable<_Wi> - struct _Ioterator { + struct _Ioterator : _Ioterator_category_base<_Wi> { // clang-format on /* [[no_unique_address]] */ _Wi _Current; - using iterator_concept = conditional_t<_Advanceable<_Wi>, random_access_iterator_tag, + using iterator_concept = conditional_t<_Advanceable<_Wi>, random_access_iterator_tag, conditional_t<_Decrementable<_Wi>, bidirectional_iterator_tag, conditional_t, forward_iterator_tag, input_iterator_tag>>>; - using iterator_category = input_iterator_tag; - using value_type = _Wi; - using difference_type = _Iota_diff_t<_Wi>; + using value_type = _Wi; + using difference_type = _Iota_diff_t<_Wi>; + + _Ioterator() requires default_initializable<_Wi> = default; + constexpr _Ioterator(const _Wi& _Value) noexcept(is_nothrow_copy_constructible_v<_Wi>) : _Current(_Value) {} _NODISCARD constexpr _Wi operator*() const noexcept(is_nothrow_copy_constructible_v<_Wi>) { return _Current; @@ -860,8 +870,10 @@ namespace ranges { } } - _NODISCARD friend constexpr bool operator==( - const _Ioterator&, const _Ioterator&) requires equality_comparable<_Wi> = default; + _NODISCARD friend constexpr bool operator==(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Left._Current == _Right._Current)) /* strengthened */ requires equality_comparable<_Wi> { + return _Left._Current == _Right._Current; + } _NODISCARD friend constexpr bool operator<(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires totally_ordered<_Wi> { return _Left._Current < _Right._Current; @@ -1257,20 +1269,18 @@ namespace ranges { /* [[no_unique_address]] */ _Vw _Range{}; /* [[no_unique_address]] */ _Copyable_box<_Pr> _Pred{}; - template // TRANSITION, LWG-3289 + template struct _Category_base {}; // clang-format off - template <_Has_member_iterator_category _Traits> - struct _Category_base<_Traits> { + template + struct _Category_base<_View> { // clang-format on using iterator_category = - conditional_t, - bidirectional_iterator_tag, - conditional_t, - forward_iterator_tag, input_iterator_tag>>; + conditional_t>, bidirectional_iterator_tag>, + bidirectional_iterator_tag, input_iterator_tag>; }; - class _Iterator : public _Category_base>> { + class _Iterator : public _Category_base<_Vw> { private: /* [[no_unique_address]] */ iterator_t<_Vw> _Current{}; filter_view* _Parent{}; @@ -1543,21 +1553,21 @@ namespace ranges { template class _Sentinel; - template // TRANSITION, LWG-3289 + template struct _Category_base {}; // clang-format off - template <_Has_member_iterator_category _Traits, class _Base> - struct _Category_base<_Traits, _Base> { + template + struct _Category_base<_Base> { // clang-format on using iterator_category = conditional_t>>, - conditional_t, - random_access_iterator_tag, typename _Traits::iterator_category>, + conditional_t>, contiguous_iterator_tag>, + random_access_iterator_tag, _Iter_cat_t>>, input_iterator_tag>; }; template - class _Iterator : public _Category_base>, _Maybe_const<_Const, _Vw>> { + class _Iterator : public _Category_base<_Maybe_const<_Const, _Vw>> { private: template friend class _Iterator; @@ -2876,34 +2886,24 @@ namespace ranges { template class _Sentinel; - template // TRANSITION, LWG-3289 + template struct _Category_base {}; - // clang-format off - template - requires _Has_member_iterator_category<_OuterTraits> && _Has_member_iterator_category<_InnerTraits> - struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_glvalue, _Inner_common> { + template + struct _Category_base<_Outer, _Inner, true> { using iterator_category = - conditional_t<_Deref_is_glvalue && _Inner_common // per LWG issue unnumbered as of 2021-03-16 - && derived_from - && derived_from, + conditional_t // per LWG issue unnumbered as of 2021-03-16 + && derived_from<_Iter_cat_t>, bidirectional_iterator_tag> // + && derived_from<_Iter_cat_t>, bidirectional_iterator_tag>, bidirectional_iterator_tag, - conditional_t<_Deref_is_glvalue - && derived_from - && derived_from, - forward_iterator_tag, - conditional_t - && derived_from, - input_iterator_tag, - output_iterator_tag>>>; + conditional_t>, forward_iterator_tag> // + && derived_from<_Iter_cat_t>, forward_iterator_tag>, + forward_iterator_tag, input_iterator_tag>>; }; - // clang-format on template - class _Iterator : public _Category_base>>, - iterator_traits>>, is_reference_v<_InnerRng<_Const>>, - common_range<_InnerRng<_Const>>> { + class _Iterator + : public _Category_base<_Maybe_const<_Const, _Vw>, _InnerRng<_Const>, is_reference_v<_InnerRng<_Const>>> { private: template friend class _Iterator; @@ -3258,10 +3258,8 @@ namespace ranges { template class _Outer_iter_base {}; - // clang-format off template class _Outer_iter_base<_Iter> { - // clang-format on protected: _Iter _Current{}; @@ -3979,18 +3977,20 @@ namespace ranges { template class _Sentinel; - template // TRANSITION, LWG-3289 + template struct _Category_base {}; - // clang-format off - template <_Has_member_iterator_category _Traits> - struct _Category_base<_Traits> { - // clang-format on - using iterator_category = typename _Traits::iterator_category; + template + struct _Category_base<_Base> { + using iterator_category = + conditional_t(*_STD declval>()))>, + input_iterator_tag, + conditional_t>, random_access_iterator_tag>, + random_access_iterator_tag, _Iter_cat_t>>>; }; template - class _Iterator : public _Category_base>> { + class _Iterator : public _Category_base<_Maybe_const<_Const, _Vw>> { private: template friend class _Iterator; diff --git a/stl/inc/xutility b/stl/inc/xutility index b550275eb9e..9d8ce1c90b9 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -516,7 +516,6 @@ concept _Cpp17_input_iterator = _Cpp17_iterator<_It> template requires (!_Has_iter_types<_It> && _Cpp17_iterator<_It> && !_Cpp17_input_iterator<_It> - // Implements the proposed resolution of LWG-3283: && (!requires { typename _It::iterator_category; } || derived_from)) struct _Iterator_traits_base<_It> { @@ -3730,15 +3729,24 @@ class move_sentinel; #endif // __cpp_lib_concepts template -class move_iterator { -public: - using iterator_type = _Iter; +struct _Move_iterator_category_base { + using iterator_category = _Iter_cat_t<_Iter>; +}; + #ifdef __cpp_lib_concepts - using iterator_concept = input_iterator_tag; +template <_Has_member_iterator_category _Iter> +struct _Move_iterator_category_base<_Iter> { using iterator_category = conditional_t, random_access_iterator_tag>, random_access_iterator_tag, _Iter_cat_t<_Iter>>; -#else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv - using iterator_category = _Iter_cat_t<_Iter>; +}; +#endif // __cpp_lib_concepts + +template +class move_iterator : public _Move_iterator_category_base<_Iter> { +public: + using iterator_type = _Iter; +#ifdef __cpp_lib_concepts + using iterator_concept = input_iterator_tag; #endif // __cpp_lib_concepts using value_type = _Iter_value_t<_Iter>; using difference_type = _Iter_diff_t<_Iter>; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 97692b89373..acf9f623cc6 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -247,7 +247,6 @@ // P2106R0 Range Algorithm Result Types // P2116R0 Removing tuple-Like Protocol Support From Fixed-Extent span // P2259R1 Repairing Input Range Adaptors And counted_iterator -// (partially implemented) // P2325R3 Views Should Not Be Required To Be Default Constructible // P????R? directory_entry::clear_cache() diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 0c11cb3051f..afe4f267af7 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -600,7 +600,7 @@ struct std::iterator_traits<::test::iterator, // conditional_t, // - conditional_t(Eq), Category, void>>; // TRANSITION, LWG-3289 + conditional_t(Eq), Category, void>>; using value_type = remove_cv_t; using difference_type = ptrdiff_t; using pointer = conditional_t, Element*, void>; diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index bf7f9fe8c0f..6a8eb893802 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -1805,7 +1805,7 @@ namespace unreachable_sentinel_test { namespace unwrap_move_only { // Validate the iterator unwrapping machinery works with move-only iterators, and that move-only iterators are not - // C++17 iterators (per the proposed resolution of LWG-3283) + // C++17 iterators template struct iter { diff --git a/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp b/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp index 8bf8494afff..05f1c3a2512 100644 --- a/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp +++ b/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp @@ -1066,7 +1066,7 @@ namespace test_subrange { template struct iterator { using iterator_concept = input_iterator_tag; - using iterator_category = void; // TRANSITION, LWG-3289 + using iterator_category = void; using value_type = int; using difference_type = int; using pointer = void; diff --git a/tests/std/tests/P0896R4_views_transform/test.cpp b/tests/std/tests/P0896R4_views_transform/test.cpp index ec8ee4e15ff..65237c83ffc 100644 --- a/tests/std/tests/P0896R4_views_transform/test.cpp +++ b/tests/std/tests/P0896R4_views_transform/test.cpp @@ -430,10 +430,14 @@ struct iterator_instantiator { conditional_t, forward_iterator_tag, input_iterator_tag>>>>); using C = typename iterator_traits::iterator_category; - STATIC_ASSERT(is_same_v>>, - conditional_t, random_access_iterator_tag, C>, - input_iterator_tag>>); + if constexpr (forward_iterator) { + STATIC_ASSERT(is_same_v>>, + conditional_t, random_access_iterator_tag, C>, + input_iterator_tag>>); + } else { + STATIC_ASSERT(!_Has_member_iterator_category); + } { // Validate iterator special member functions and base STATIC_ASSERT(default_initializable == default_initializable); From f7e1671964592dc0514ecdb0a6cfc4bed274a46d Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 29 Jun 2021 19:30:10 -0700 Subject: [PATCH 02/13] WIP --- stl/inc/iterator | 19 ++++++---- stl/inc/xutility | 22 +++++++++--- tests/std/include/range_algorithm_support.hpp | 35 ++++++++++++++----- .../tests/P0896R4_common_iterator/test.cpp | 31 ++++++++++++++++ .../test.cpp | 13 +++---- .../test.compile.pass.cpp | 1 - 6 files changed, 94 insertions(+), 27 deletions(-) diff --git a/stl/inc/iterator b/stl/inc/iterator index 487af063f5b..3212a021ce9 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -1026,15 +1026,20 @@ struct _Common_iterator_pointer_type<_Iter> { using pointer = decltype(_STD declval<_Iter&>().operator->()); }; +template +concept _Has_forward_category = requires { + typename _Iter_cat_t<_Iter>; + requires derived_from<_Iter_cat_t<_Iter>, forward_iterator_tag>; +}; + template struct iterator_traits> { - using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; - using iterator_category = - conditional_t, forward_iterator_tag>, forward_iterator_tag, input_iterator_tag>; - using value_type = iter_value_t<_Iter>; - using difference_type = iter_difference_t<_Iter>; - using pointer = typename _Common_iterator_pointer_type<_Iter>::pointer; - using reference = iter_reference_t<_Iter>; + using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; + using iterator_category = conditional_t<_Has_forward_category<_Iter>, forward_iterator_tag, input_iterator_tag>; + using value_type = iter_value_t<_Iter>; + using difference_type = iter_difference_t<_Iter>; + using pointer = typename _Common_iterator_pointer_type<_Iter>::pointer; + using reference = iter_reference_t<_Iter>; }; // CLASS TEMPLATE counted_iterator diff --git a/stl/inc/xutility b/stl/inc/xutility index b550275eb9e..4d184c70e64 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3727,16 +3727,30 @@ struct _Default_sentinel {}; // empty struct to serve as the end of a range #ifdef __cpp_lib_concepts template class move_sentinel; + +template +struct _Move_iterator_category {}; + +// clang-format off +template + requires requires { typename _Iter_cat_t<_Iter>; } +struct _Move_iterator_category<_Iter> { + using iterator_category = conditional_t, random_access_iterator_tag>, + random_access_iterator_tag, _Iter_cat_t<_Iter>>; +}; +// clang-format on #endif // __cpp_lib_concepts template -class move_iterator { +class move_iterator +#ifdef __cpp_lib_concepts + : public _Move_iterator_category<_Iter> +#endif // __cpp_lib_concepts +{ public: using iterator_type = _Iter; #ifdef __cpp_lib_concepts - using iterator_concept = input_iterator_tag; - using iterator_category = conditional_t, random_access_iterator_tag>, - random_access_iterator_tag, _Iter_cat_t<_Iter>>; + using iterator_concept = input_iterator_tag; #else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv using iterator_category = _Iter_cat_t<_Iter>; #endif // __cpp_lib_concepts diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 0c11cb3051f..005e62031f8 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -592,19 +592,36 @@ namespace test { } }; // clang-format on + + template + struct iterator_traits_base {}; + + template + struct iterator_traits_base { + using iterator_category = Category; + }; + + template + struct iterator_traits_base { + using iterator_category = input; + }; + + template + struct iterator_traits_base { + using iterator_category = input; + }; } // namespace test template -struct std::iterator_traits<::test::iterator> { - using iterator_concept = Category; - using iterator_category = conditional_t, // - conditional_t, // - conditional_t(Eq), Category, void>>; // TRANSITION, LWG-3289 - using value_type = remove_cv_t; - using difference_type = ptrdiff_t; - using pointer = conditional_t, Element*, void>; - using reference = iter_reference_t<::test::iterator>; +struct std::iterator_traits<::test::iterator> + : ::test::iterator_traits_base, + Proxy == ::test::ProxyRef::yes, Eq == ::test::CanCompare::yes> { + using iterator_concept = Category; + using value_type = remove_cv_t; + using difference_type = ptrdiff_t; + using pointer = conditional_t, Element*, void>; + using reference = iter_reference_t<::test::iterator>; }; template diff --git a/tests/std/tests/P0896R4_common_iterator/test.cpp b/tests/std/tests/P0896R4_common_iterator/test.cpp index 1c7b93b40ad..7bd18751a23 100644 --- a/tests/std/tests/P0896R4_common_iterator/test.cpp +++ b/tests/std/tests/P0896R4_common_iterator/test.cpp @@ -194,6 +194,37 @@ bool test_operator_arrow() { return true; } +// After P2259, common_iterator supports "copyable but not equality_comparable" iterators, which combination +// test::iterator does not provide (I don't think this is a combination of properties that any real iterator will ever +// exhibit). Whip up something so we can test the iterator_category metaprogramming. +// clang-format off +template +concept no_iterator_traits = !requires { typename iterator_traits::iterator_concept; } + && !requires { typename iterator_traits::iterator_category; } + && !requires { typename iterator_traits::value_type; } + && !requires { typename iterator_traits::difference_type; } + && !requires { typename iterator_traits::pointer; } + && !requires { typename iterator_traits::reference; }; +// clang-format on + +struct input_copy_but_no_eq { + using value_type = int; + using difference_type = int; + + input_copy_but_no_eq() = delete; + + int operator*() const; + input_copy_but_no_eq& operator++(); + void operator++(int); + + bool operator==(default_sentinel_t) const; +}; +STATIC_ASSERT(input_iterator); +STATIC_ASSERT(no_iterator_traits); +STATIC_ASSERT(sentinel_for); +using ICID = iterator_traits>; +STATIC_ASSERT(same_as); + int main() { with_writable_iterators::call(); diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index bf7f9fe8c0f..80b0217191e 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -1805,7 +1805,7 @@ namespace unreachable_sentinel_test { namespace unwrap_move_only { // Validate the iterator unwrapping machinery works with move-only iterators, and that move-only iterators are not - // C++17 iterators (per the proposed resolution of LWG-3283) + // C++17 iterators template struct iter { @@ -3082,11 +3082,10 @@ namespace move_iterator_test { template struct input_iter { - using iterator_concept = input_iterator_tag; - using iterator_category = void; - using value_type = int; - using difference_type = int; - using pointer = void; + using iterator_concept = input_iterator_tag; + using value_type = int; + using difference_type = int; + using pointer = void; struct reference { operator int() const; @@ -3164,6 +3163,8 @@ namespace move_iterator_test { STATIC_ASSERT(same_as>::iterator_category, forward_iterator_tag>); STATIC_ASSERT(same_as::iterator_concept, input_iterator_tag>); STATIC_ASSERT(same_as::iterator_category, input_iterator_tag>); + STATIC_ASSERT(!has_member_iter_category>>); + STATIC_ASSERT(!has_member_iter_category>>); // Validate that move_iterator::reference is iter_rvalue_reference_t STATIC_ASSERT(same_as>::reference, input_iter::rvalue_reference>); diff --git a/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp b/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp index 8bf8494afff..b1a43395b2d 100644 --- a/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp +++ b/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp @@ -1066,7 +1066,6 @@ namespace test_subrange { template struct iterator { using iterator_concept = input_iterator_tag; - using iterator_category = void; // TRANSITION, LWG-3289 using value_type = int; using difference_type = int; using pointer = void; From 6e7eace63853fd6669bb7a2dab3f05e08e707ca2 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 16 Jul 2021 13:34:56 -0700 Subject: [PATCH 03/13] test fallout --- tests/std/tests/P0896R4_views_transform/test.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/std/tests/P0896R4_views_transform/test.cpp b/tests/std/tests/P0896R4_views_transform/test.cpp index 65237c83ffc..52fde914bdd 100644 --- a/tests/std/tests/P0896R4_views_transform/test.cpp +++ b/tests/std/tests/P0896R4_views_transform/test.cpp @@ -429,14 +429,9 @@ struct iterator_instantiator { conditional_t, bidirectional_iterator_tag, conditional_t, forward_iterator_tag, input_iterator_tag>>>>); - using C = typename iterator_traits::iterator_category; + STATIC_ASSERT(_Has_member_iterator_category == forward_iterator); if constexpr (forward_iterator) { - STATIC_ASSERT(is_same_v>>, - conditional_t, random_access_iterator_tag, C>, - input_iterator_tag>>); - } else { - STATIC_ASSERT(!_Has_member_iterator_category); + STATIC_ASSERT(is_same_v); } { // Validate iterator special member functions and base From 24f696658b73baa8056e5e2b9469c00018d78af8 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sun, 18 Jul 2021 08:00:57 +0200 Subject: [PATCH 04/13] Apply suggestions from code review Co-authored-by: Casey Carter --- stl/inc/ranges | 2 +- stl/inc/xutility | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index e0fab9a66b9..2ee2f5cf2df 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2892,7 +2892,7 @@ namespace ranges { template struct _Category_base<_Outer, _Inner, true> { using iterator_category = - conditional_t // per LWG issue unnumbered as of 2021-03-16 + conditional_t // per LWG-3535 && derived_from<_Iter_cat_t>, bidirectional_iterator_tag> // && derived_from<_Iter_cat_t>, bidirectional_iterator_tag>, bidirectional_iterator_tag, diff --git a/stl/inc/xutility b/stl/inc/xutility index c1a77d0037b..ba650192d1a 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1046,7 +1046,7 @@ concept indirectly_movable_storable = indirectly_movable<_In, _Out> && indirectly_writable<_Out, iter_value_t<_In>> && movable> && constructible_from, iter_rvalue_reference_t<_In>> - && assignable_from&, iter_rvalue_reference_t<_In> >; + && assignable_from&, iter_rvalue_reference_t<_In>>; // CONCEPT indirectly_copyable template From fab7f47a74bf0515ec8a705bebf913126536e7e9 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 3 Aug 2021 22:17:14 +0200 Subject: [PATCH 05/13] Make _Iterator a aggregate --- stl/inc/ranges | 18 +++++++----------- tests/std/tests/P0896R4_views_iota/test.cpp | 8 ++++---- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index f21784f8392..665b42079b6 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -783,12 +783,6 @@ namespace ranges { using value_type = _Wi; using difference_type = _Iota_diff_t<_Wi>; - // clang-format off - _Ioterator() requires default_initializable<_Wi> = default; - // clang-format on - - constexpr _Ioterator(const _Wi& _Value) noexcept(is_nothrow_copy_constructible_v<_Wi>) : _Current(_Value) {} - _NODISCARD constexpr _Wi operator*() const noexcept(is_nothrow_copy_constructible_v<_Wi>) { return _Current; } @@ -923,15 +917,15 @@ namespace ranges { _NODISCARD friend constexpr _Ioterator operator+(_Ioterator _It, const difference_type _Off) noexcept( noexcept(static_cast<_Wi>(_It._Current + _Off))) /* strengthened */ requires _Advanceable<_Wi> { - return _Ioterator{static_cast<_Wi>(_It._Current + _Off)}; + return _Ioterator{{}, static_cast<_Wi>(_It._Current + _Off)}; } _NODISCARD friend constexpr _Ioterator operator+(const difference_type _Off, _Ioterator _It) noexcept( noexcept(static_cast<_Wi>(_It._Current + _Off))) /* strengthened */ requires _Advanceable<_Wi> { - return _Ioterator{static_cast<_Wi>(_It._Current + _Off)}; + return _Ioterator{{}, static_cast<_Wi>(_It._Current + _Off)}; } _NODISCARD friend constexpr _Ioterator operator-(_Ioterator _It, const difference_type _Off) noexcept( noexcept(static_cast<_Wi>(_It._Current - _Off))) /* strengthened */ requires _Advanceable<_Wi> { - return _Ioterator{static_cast<_Wi>(_It._Current - _Off)}; + return _Ioterator{{}, static_cast<_Wi>(_It._Current - _Off)}; } _NODISCARD friend constexpr difference_type operator-(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( @@ -1023,11 +1017,13 @@ namespace ranges { : _Value(_STD move(_First._Current)), _Bound(_STD move(_Bound_from(_Last))) {} _NODISCARD constexpr _It begin() const noexcept(is_nothrow_copy_constructible_v<_Wi>) /* strengthened */ { - return _It{_Value}; + return _It{{}, _Value}; } _NODISCARD constexpr _Se end() const noexcept(is_nothrow_copy_constructible_v<_Bo>) /* strengthened */ { - if constexpr (same_as<_Bo, unreachable_sentinel_t>) { + if constexpr (same_as<_Wi, _Bo>) { + return _It{{}, _Bound}; + } else if constexpr (same_as<_Bo, unreachable_sentinel_t>) { return unreachable_sentinel; } else { return _Se{_Bound}; diff --git a/tests/std/tests/P0896R4_views_iota/test.cpp b/tests/std/tests/P0896R4_views_iota/test.cpp index ed990295ba3..5d26bcca000 100644 --- a/tests/std/tests/P0896R4_views_iota/test.cpp +++ b/tests/std/tests/P0896R4_views_iota/test.cpp @@ -75,7 +75,7 @@ constexpr void test_integral() { static_assert(same_as); static_assert(same_as::iterator_category, input_iterator_tag>); - assert(I{} == I{T{0}}); + assert(I{} == (I{{}, T{0}})); static_assert(is_nothrow_default_constructible_v); assert(I{} == I{}); assert(!(I{} != I{})); @@ -93,9 +93,9 @@ constexpr void test_integral() { assert(*second == T{1}); static_assert(noexcept(*first)); - assert(first == I{T{0}}); - assert(second == I{T{1}}); - static_assert(noexcept(noexcept(I{T{0}}))); // strengthened + assert(first == (I{{}, T{0}})); + assert(second == (I{{}, T{1}})); + static_assert(noexcept(noexcept(I{{}, T{0}}))); // strengthened { I tmp = first; From 52cbf139742c1e1b7daabeee6f796b2513c08d2b Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 3 Aug 2021 22:18:31 +0200 Subject: [PATCH 06/13] Fix filter_view iterator category --- stl/inc/ranges | 4 +++- .../std/tests/P0896R4_views_filter_iterator/test.cpp | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 665b42079b6..08bd7daa6d4 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1298,7 +1298,9 @@ namespace ranges { // clang-format on using iterator_category = conditional_t>, bidirectional_iterator_tag>, - bidirectional_iterator_tag, input_iterator_tag>; + bidirectional_iterator_tag, + conditional_t>, forward_iterator_tag>, + forward_iterator_tag, _Iter_cat_t>>>; }; class _Iterator : public _Category_base<_Vw> { diff --git a/tests/std/tests/P0896R4_views_filter_iterator/test.cpp b/tests/std/tests/P0896R4_views_filter_iterator/test.cpp index 7098949ada5..12f09a3c033 100644 --- a/tests/std/tests/P0896R4_views_filter_iterator/test.cpp +++ b/tests/std/tests/P0896R4_views_filter_iterator/test.cpp @@ -30,10 +30,13 @@ struct iterator_instantiator { conditional_t, bidirectional_iterator_tag, conditional_t, forward_iterator_tag, input_iterator_tag>>>); - using C = typename iterator_traits::iterator_category; - static_assert(is_same_v, bidirectional_iterator_tag, - conditional_t, forward_iterator_tag, input_iterator_tag>>>); + static_assert(_Has_member_iterator_category == forward_iterator); + if constexpr (forward_iterator) { + using C = typename iterator_traits::iterator_category; + static_assert(is_same_v, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>>); + } { // Validate iterator special member functions and base static_assert(default_initializable == default_initializable); From 4cd172d5f2c8e90c2b372a6090898b027565cb02 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 3 Aug 2021 22:18:57 +0200 Subject: [PATCH 07/13] Properly test against prvalue for elements_view --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 08bd7daa6d4..90d8027ce6c 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -4018,7 +4018,7 @@ namespace ranges { template struct _Category_base<_Base> { using iterator_category = - conditional_t(*_STD declval>()))>, + conditional_t(*_STD declval>()))>, input_iterator_tag, conditional_t>, random_access_iterator_tag>, random_access_iterator_tag, _Iter_cat_t>>>; From 3069ebfd1bf15e4c1ff1dda8aab0abd07773b50f Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 27 Jul 2021 14:56:48 -0700 Subject: [PATCH 08/13] Remove implementation of LWG-3283 ... which has been resolved as NAD after adoption of P2259. Drive-by: Correctly order the `iterator_traits` nested types in three places where they are misordered. (There's some value in using the Standard's consistent ordering.) --- stl/inc/xutility | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 81ea3d794d7..2e607d28ceb 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -475,8 +475,8 @@ struct _Old_iter_traits_pointer { template <_Has_iter_types _It> struct _Iterator_traits_base<_It> { using iterator_category = typename _It::iterator_category; - using difference_type = typename _It::difference_type; using value_type = typename _It::value_type; + using difference_type = typename _It::difference_type; using pointer = typename _Old_iter_traits_pointer<_Has_member_pointer<_It>>::template _Apply<_It>; using reference = typename _It::reference; }; @@ -515,14 +515,12 @@ concept _Cpp17_input_iterator = _Cpp17_iterator<_It> }; template - requires (!_Has_iter_types<_It> && _Cpp17_iterator<_It> && !_Cpp17_input_iterator<_It> - && (!requires { typename _It::iterator_category; } - || derived_from)) + requires (!_Has_iter_types<_It> && _Cpp17_iterator<_It> && !_Cpp17_input_iterator<_It>) struct _Iterator_traits_base<_It> { using iterator_category = output_iterator_tag; + using value_type = void; using difference_type = typename _Iter_traits_difference<_Has_member_difference_type>>::template _Apply<_It>; - using value_type = void; using pointer = void; using reference = void; }; @@ -652,8 +650,8 @@ template requires (!_Has_iter_types<_It> && _Cpp17_input_iterator<_It>) struct _Iterator_traits_base<_It> { using iterator_category = typename _Iter_traits_category<_Has_member_iterator_category<_It>>::template _Apply<_It>; - using difference_type = typename incrementable_traits<_It>::difference_type; using value_type = typename indirectly_readable_traits<_It>::value_type; + using difference_type = typename incrementable_traits<_It>::difference_type; using pointer = typename _Iter_traits_pointer<( _Has_member_pointer<_It> ? _Itraits_pointer_strategy::_Use_member : _Has_member_arrow<_It&> ? _Itraits_pointer_strategy::_Use_decltype From 91aea86f686209356ed841023dc3af00d2ad7792 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 2 Aug 2021 14:05:35 -0700 Subject: [PATCH 09/13] Address Tim's comment on my comment --- tests/std/tests/P0896R4_common_iterator/test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P0896R4_common_iterator/test.cpp b/tests/std/tests/P0896R4_common_iterator/test.cpp index 7bd18751a23..4736c7543aa 100644 --- a/tests/std/tests/P0896R4_common_iterator/test.cpp +++ b/tests/std/tests/P0896R4_common_iterator/test.cpp @@ -194,9 +194,9 @@ bool test_operator_arrow() { return true; } -// After P2259, common_iterator supports "copyable but not equality_comparable" iterators, which combination -// test::iterator does not provide (I don't think this is a combination of properties that any real iterator will ever -// exhibit). Whip up something so we can test the iterator_category metaprogramming. +// common_iterator supports "copyable but not equality_comparable" iterators, which combination test::iterator does not +// provide (I don't think this is a combination of properties that any real iterator will ever exhibit). Whip up +// something so we can test the iterator_category metaprogramming. // clang-format off template concept no_iterator_traits = !requires { typename iterator_traits::iterator_concept; } From bbe520ab94d8eeb54fa1eead8df21aefd954d602 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 12 Aug 2021 13:31:00 -0700 Subject: [PATCH 10/13] Bring iota_view::iterator (plain _Ioterator for us) into compliance ... by adding constructors and giving up on the code generation benefits of being ABI-aggregate. Adding a defaulted `<=>` to the partial specializations of the base class `_Ioterator_category_base` allows us to go back to defaulting `==` and `<=>`. --- stl/inc/ranges | 44 +++++++++++++-------- tests/std/tests/P0896R4_views_iota/test.cpp | 8 ++-- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index fa80235d771..251afac0de6 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -777,18 +777,22 @@ namespace ranges { }; template - struct _Ioterator_category_base {}; + struct _Ioterator_category_base { + auto operator<=>(const _Ioterator_category_base&) const = default; + }; template struct _Ioterator_category_base<_Wi> { using iterator_category = input_iterator_tag; + + auto operator<=>(const _Ioterator_category_base&) const = default; }; template requires copyable<_Wi> struct _Ioterator : _Ioterator_category_base<_Wi> { // clang-format on - /* [[no_unique_address]] */ _Wi _Current; + /* [[no_unique_address]] */ _Wi _Current{}; using iterator_concept = conditional_t<_Advanceable<_Wi>, random_access_iterator_tag, conditional_t<_Decrementable<_Wi>, bidirectional_iterator_tag, @@ -796,6 +800,13 @@ namespace ranges { using value_type = _Wi; using difference_type = _Iota_diff_t<_Wi>; + // clang-format off + _Ioterator() requires default_initializable<_Wi> = default; + // clang-format on + + constexpr explicit _Ioterator(_Wi _Val) noexcept(is_nothrow_move_constructible_v<_Wi>) /* strengthened */ + : _Current(_STD move(_Val)) {} + _NODISCARD constexpr _Wi operator*() const noexcept(is_nothrow_copy_constructible_v<_Wi>) { return _Current; } @@ -900,10 +911,11 @@ namespace ranges { } } - _NODISCARD friend constexpr bool operator==(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( - noexcept(_Left._Current == _Right._Current)) /* strengthened */ requires equality_comparable<_Wi> { - return _Left._Current == _Right._Current; - } + // clang-format off + _NODISCARD friend constexpr bool operator==( + const _Ioterator&, const _Ioterator&) requires equality_comparable<_Wi> = default; + // clang-format on + _NODISCARD friend constexpr bool operator<(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires totally_ordered<_Wi> { return _Left._Current < _Right._Current; @@ -920,25 +932,23 @@ namespace ranges { noexcept(!(_Left._Current < _Right._Current))) /* strengthened */ requires totally_ordered<_Wi> { return !(_Left._Current < _Right._Current); } + // clang-format off - _NODISCARD friend constexpr auto operator<=>(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( - noexcept(_Left._Current <=> _Right._Current)) /* strengthened */ - requires totally_ordered<_Wi> && three_way_comparable<_Wi> { - // clang-format on - return _Left._Current <=> _Right._Current; - } + _NODISCARD friend constexpr auto operator<=>(const _Ioterator& _Left, const _Ioterator& _Right) + requires totally_ordered<_Wi> && three_way_comparable<_Wi> = default; + // clang-format on _NODISCARD friend constexpr _Ioterator operator+(_Ioterator _It, const difference_type _Off) noexcept( noexcept(static_cast<_Wi>(_It._Current + _Off))) /* strengthened */ requires _Advanceable<_Wi> { - return _Ioterator{{}, static_cast<_Wi>(_It._Current + _Off)}; + return _Ioterator{static_cast<_Wi>(_It._Current + _Off)}; } _NODISCARD friend constexpr _Ioterator operator+(const difference_type _Off, _Ioterator _It) noexcept( noexcept(static_cast<_Wi>(_It._Current + _Off))) /* strengthened */ requires _Advanceable<_Wi> { - return _Ioterator{{}, static_cast<_Wi>(_It._Current + _Off)}; + return _Ioterator{static_cast<_Wi>(_It._Current + _Off)}; } _NODISCARD friend constexpr _Ioterator operator-(_Ioterator _It, const difference_type _Off) noexcept( noexcept(static_cast<_Wi>(_It._Current - _Off))) /* strengthened */ requires _Advanceable<_Wi> { - return _Ioterator{{}, static_cast<_Wi>(_It._Current - _Off)}; + return _Ioterator{static_cast<_Wi>(_It._Current - _Off)}; } _NODISCARD friend constexpr difference_type operator-(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( @@ -1030,12 +1040,12 @@ namespace ranges { : _Value(_STD move(_First._Current)), _Bound(_STD move(_Bound_from(_Last))) {} _NODISCARD constexpr _It begin() const noexcept(is_nothrow_copy_constructible_v<_Wi>) /* strengthened */ { - return _It{{}, _Value}; + return _It{_Value}; } _NODISCARD constexpr _Se end() const noexcept(is_nothrow_copy_constructible_v<_Bo>) /* strengthened */ { if constexpr (same_as<_Wi, _Bo>) { - return _It{{}, _Bound}; + return _It{_Bound}; } else if constexpr (same_as<_Bo, unreachable_sentinel_t>) { return unreachable_sentinel; } else { diff --git a/tests/std/tests/P0896R4_views_iota/test.cpp b/tests/std/tests/P0896R4_views_iota/test.cpp index 5d26bcca000..0c2ebd0f754 100644 --- a/tests/std/tests/P0896R4_views_iota/test.cpp +++ b/tests/std/tests/P0896R4_views_iota/test.cpp @@ -75,7 +75,7 @@ constexpr void test_integral() { static_assert(same_as); static_assert(same_as::iterator_category, input_iterator_tag>); - assert(I{} == (I{{}, T{0}})); + assert(I{} == (I{T{0}})); static_assert(is_nothrow_default_constructible_v); assert(I{} == I{}); assert(!(I{} != I{})); @@ -93,9 +93,9 @@ constexpr void test_integral() { assert(*second == T{1}); static_assert(noexcept(*first)); - assert(first == (I{{}, T{0}})); - assert(second == (I{{}, T{1}})); - static_assert(noexcept(noexcept(I{{}, T{0}}))); // strengthened + assert(first == (I{T{0}})); + assert(second == (I{T{1}})); + static_assert(noexcept(noexcept(I{T{0}}))); // strengthened { I tmp = first; From 93f942e2cdb327ed3c7bc3db93ef58b86c40a1ee Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 12 Aug 2021 13:37:23 -0700 Subject: [PATCH 11/13] Remove parentheses in `assert`s ... that I made redundant in the last commit. --- tests/std/tests/P0896R4_views_iota/test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P0896R4_views_iota/test.cpp b/tests/std/tests/P0896R4_views_iota/test.cpp index 0c2ebd0f754..ed990295ba3 100644 --- a/tests/std/tests/P0896R4_views_iota/test.cpp +++ b/tests/std/tests/P0896R4_views_iota/test.cpp @@ -75,7 +75,7 @@ constexpr void test_integral() { static_assert(same_as); static_assert(same_as::iterator_category, input_iterator_tag>); - assert(I{} == (I{T{0}})); + assert(I{} == I{T{0}}); static_assert(is_nothrow_default_constructible_v); assert(I{} == I{}); assert(!(I{} != I{})); @@ -93,8 +93,8 @@ constexpr void test_integral() { assert(*second == T{1}); static_assert(noexcept(*first)); - assert(first == (I{T{0}})); - assert(second == (I{T{1}})); + assert(first == I{T{0}}); + assert(second == I{T{1}}); static_assert(noexcept(noexcept(I{T{0}}))); // strengthened { From 1d083ca46e516b7c10ad7c87c508311b7aa144dd Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 13 Aug 2021 11:01:22 -0700 Subject: [PATCH 12/13] Fix bug that causes test::iterator to sometimes lie about category --- 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 93af0325f27..dad6843ab83 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -615,7 +615,7 @@ namespace test { template struct std::iterator_traits<::test::iterator> - : ::test::iterator_traits_base, + : ::test::iterator_traits_base, Proxy == ::test::ProxyRef::yes, Eq == ::test::CanCompare::yes> { using iterator_concept = Category; using value_type = remove_cv_t; From 0ee79a0fbe074dc8e4138c75faec9eede5353735 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 13 Aug 2021 11:07:10 -0700 Subject: [PATCH 13/13] Consistently use _NODISCARD for defaulted <=> ... even in stateless classes for which `<=>` is a constant function. --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 251afac0de6..d4c9860429f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -778,14 +778,14 @@ namespace ranges { template struct _Ioterator_category_base { - auto operator<=>(const _Ioterator_category_base&) const = default; + _NODISCARD auto operator<=>(const _Ioterator_category_base&) const = default; }; template struct _Ioterator_category_base<_Wi> { using iterator_category = input_iterator_tag; - auto operator<=>(const _Ioterator_category_base&) const = default; + _NODISCARD auto operator<=>(const _Ioterator_category_base&) const = default; }; template