From 4ccfc5eb7bc3d639d9c0dc8764e2951e22c772f3 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Mon, 13 Feb 2023 19:04:14 +0800 Subject: [PATCH 01/22] Revert "``: Temporarily disable `join_view` for non-`forward_range`s, pending resolution of LWG-3698 (#2727)" This reverts commit 296681379db18e0aeac36f2da3c416b811a176b3. --- stl/inc/ranges | 13 ------------- tests/libcxx/usual_matrix.lst | 2 +- tests/std/tests/P0896R4_views_join/test.cpp | 2 -- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 78b1c04e432..9d4be1bb845 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3566,19 +3566,6 @@ namespace ranges { _EXPORT_STD template requires view<_Vw> && input_range> class join_view : public _Join_view_base<_Vw> { - -#ifndef _USE_JOIN_VIEW_INPUT_RANGE - static_assert(forward_range<_Vw>, - "Due to a design flaw, join_view can misbehave " - "with some input-only ranges (see https://wg21.link/lwg3698). " - "We believe that WG21 will be unable to fix this problem without breaking ABI. " - "To minimize breakage when a fix is implemented, " - "we are temporarily disabling potentially problematic cases. " - "You can define _USE_JOIN_VIEW_INPUT_RANGE to suppress this error, " - "but be aware that you will almost certainly need to recompile " - "when we release a fix."); -#endif // _USE_JOIN_VIEW_INPUT_RANGE - private: template using _InnerRng = range_reference_t<_Maybe_const<_Const, _Vw>>; diff --git a/tests/libcxx/usual_matrix.lst b/tests/libcxx/usual_matrix.lst index 4a66aa7f1f1..5604d27fed6 100644 --- a/tests/libcxx/usual_matrix.lst +++ b/tests/libcxx/usual_matrix.lst @@ -3,7 +3,7 @@ RUNALL_INCLUDE ..\universal_prefix.lst RUNALL_CROSSLIST -PM_CL="/EHsc /MTd /std:c++latest /permissive- /utf-8 /FImsvc_stdlib_force_include.h /wd4643 /D_STL_CALL_ABORT_INSTEAD_OF_INVALID_PARAMETER /D_USE_JOIN_VIEW_INPUT_RANGE" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /utf-8 /FImsvc_stdlib_force_include.h /wd4643 /D_STL_CALL_ABORT_INSTEAD_OF_INVALID_PARAMETER" RUNALL_CROSSLIST PM_CL="/analyze:autolog- /Zc:preprocessor /wd6262" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call" diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 7da95ce34bc..896a3d03f4a 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#define _USE_JOIN_VIEW_INPUT_RANGE - #include #include #include From 259eba9402223be1306574ac0c8e57701299d1fc Mon Sep 17 00:00:00 2001 From: cpplearner Date: Mon, 13 Feb 2023 18:31:15 +0800 Subject: [PATCH 02/22] LWG-3698 (join_view part) --- stl/inc/ranges | 99 ++++++++++++++++----- tests/std/tests/P0896R4_views_join/test.cpp | 5 +- 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 9d4be1bb845..dba648569aa 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3556,16 +3556,25 @@ namespace ranges { #ifdef __clang__ template // TRANSITION, LLVM-47414 - concept _Can_const_join = input_range && is_reference_v>; + concept _Can_const_join = forward_range && is_reference_v>; #endif // ^^^ workaround ^^^ template requires is_reference_v> class _Join_view_base<_Vw> : public view_interface> {}; + template + class _Join_view_outer_iter_base : public _Join_view_base<_Vw> { + protected: + _Non_propagating_cache> _Outer; + }; + + template + class _Join_view_outer_iter_base<_Vw> : public _Join_view_base<_Vw> {}; + _EXPORT_STD template requires view<_Vw> && input_range> - class join_view : public _Join_view_base<_Vw> { + class join_view : public _Join_view_outer_iter_base<_Vw> { private: template using _InnerRng = range_reference_t<_Maybe_const<_Const, _Vw>>; @@ -3590,14 +3599,31 @@ namespace ranges { }; template - class _Iterator + class _Iterator_base + : public _Category_base<_Maybe_const<_Const, _Vw>, _InnerRng<_Const>, is_reference_v<_InnerRng<_Const>>> {}; + + template + requires forward_range<_Vw> + class _Iterator_base<_Const> : public _Category_base<_Maybe_const<_Const, _Vw>, _InnerRng<_Const>, is_reference_v<_InnerRng<_Const>>> { + protected: + using _OuterIter = iterator_t<_Maybe_const<_Const, _Vw>>; + + _Iterator_base() = default; + constexpr explicit _Iterator_base(_OuterIter&& _Outer_) : _Outer{_STD move(_Outer_)} {} + + /* [[no_unique_address]] */ _OuterIter _Outer{}; + }; + + template + class _Iterator : public _Iterator_base<_Const> { private: template friend class _Iterator; template friend class _Sentinel; + using _Mybase = _Iterator_base<_Const>; using _Parent_t = _Maybe_const<_Const, join_view>; using _Base = _Maybe_const<_Const, _Vw>; using _OuterIter = iterator_t<_Base>; @@ -3606,21 +3632,37 @@ namespace ranges { // 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<_InnerRng<_Const>>; - /* [[no_unique_address]] */ _OuterIter _Outer{}; /* [[no_unique_address]] */ _Defaultabox<_InnerIter> _Inner{}; // Non-standard extension: when _Inner_iter // is default-constructible, we don't wrap in // an optional-like. _Parent_t* _Parent{}; + _NODISCARD constexpr _OuterIter& _Get_outer() noexcept { + if constexpr (forward_range<_Base>) { + return this->_Outer; + } else { + return *_Parent->_Outer; + } + } + + _NODISCARD constexpr const _OuterIter& _Get_outer() const noexcept { + if constexpr (forward_range<_Base>) { + return this->_Outer; + } else { + return *_Parent->_Outer; + } + } + constexpr auto&& _Update_inner() { if constexpr (_Deref_is_glvalue) { - return *_Outer; + return *_Get_outer(); } else { - return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Outer)._Val; + return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Get_outer())._Val; } } constexpr void _Satisfy() { + auto& _Outer = _Get_outer(); const auto _Last = _RANGES end(_Parent->_Range); for (; _Outer != _Last; ++_Outer) { auto&& _Tmp = _Update_inner(); @@ -3637,10 +3679,10 @@ 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 join_view end iterator"); + _STL_VERIFY(_Get_outer() != _RANGES end(_Parent->_Range), "cannot dereference join_view end iterator"); sentinel_t<_InnerRng<_Const>> _Last; if constexpr (_Deref_is_glvalue) { - _Last = _RANGES end(*_Outer); + _Last = _RANGES end(*_Get_outer()); } else { _Last = _RANGES end((*_Parent->_Inner)._Val); } @@ -3667,13 +3709,18 @@ namespace ranges { _Iterator() requires default_initializable<_OuterIter> = default; // clang-format on + constexpr explicit _Iterator(_Parent_t& _Parent) + requires (!forward_range<_Base>) + : _Parent{_STD addressof(_Parent)} { + _Satisfy(); + } + constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) - : _Outer{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { + requires forward_range<_Base> + : _Mybase{_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(this->_Outer, _RANGES end(_Parent_._Range)); + _Adl_verify_range(_RANGES begin(_Parent_._Range), this->_Outer); #endif // _ITERATOR_DEBUG_LEVEL != 0 _Satisfy(); } @@ -3681,7 +3728,7 @@ namespace ranges { 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} {} + : _Mybase{_STD move(_It._Outer)}, _Inner{_STD move(_It._Inner)}, _Parent{_It._Parent} {} _NODISCARD constexpr decltype(auto) operator*() const noexcept(noexcept(**_Inner)) /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3701,6 +3748,7 @@ namespace ranges { } constexpr _Iterator& operator++() { + auto& _Outer = _Get_outer(); if constexpr (_Deref_is_glvalue) { if (++*_Inner == _RANGES end(*_Outer)) { ++_Outer; @@ -3730,6 +3778,7 @@ namespace ranges { requires _Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_InnerRng<_Const>> && common_range<_InnerRng<_Const>> { // clang-format on + auto& _Outer = _Get_outer(); if (_Outer == _RANGES end(_Parent->_Range)) { --_Outer; _Inner = _RANGES end(*_Outer); @@ -3756,7 +3805,7 @@ namespace ranges { _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( noexcept(_Fake_copy_init(_Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner))) /* strengthened */ - requires _Deref_is_glvalue && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { + requires _Deref_is_glvalue && forward_range<_Base> && equality_comparable<_InnerIter> { // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Same_range(_Right); @@ -3805,9 +3854,9 @@ namespace ranges { template requires sentinel_for, _Maybe_const_iter<_OtherConst>> _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const noexcept( - noexcept(_Fake_copy_init(_It._Outer == _Last))) { + noexcept(_Fake_copy_init(_It._Get_outer() == _Last))) { // clang-format on - return _It._Outer == _Last; + return _It._Get_outer() == _Last; } public: @@ -3850,15 +3899,20 @@ namespace ranges { } _NODISCARD constexpr auto begin() { - constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng>; - return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + if constexpr (forward_range<_Vw>) { + constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng>; + return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + } else { + this->_Outer._Emplace(_RANGES begin(_Range)); + return _Iterator{*this}; + } } _NODISCARD constexpr _Iterator begin() const #ifdef __clang__ // TRANSITION, LLVM-47414 requires _Can_const_join<_Vw> #else // ^^^ workaround / no workaround vvv - requires input_range && is_reference_v<_InnerRng> + requires forward_range && is_reference_v<_InnerRng> #endif // TRANSITION, LLVM-47414 { return _Iterator{*this, _RANGES begin(_Range)}; @@ -3877,11 +3931,10 @@ namespace ranges { #ifdef __clang__ // TRANSITION, LLVM-47414 requires _Can_const_join<_Vw> #else // ^^^ workaround / no workaround vvv - requires input_range && is_reference_v<_InnerRng> + requires forward_range && is_reference_v<_InnerRng> #endif // TRANSITION, LLVM-47414 { - if constexpr (forward_range && forward_range<_InnerRng> && common_range - && common_range<_InnerRng>) { + if constexpr (forward_range<_InnerRng> && common_range && common_range<_InnerRng>) { return _Iterator{*this, _RANGES end(_Range)}; } else { return _Sentinel{*this}; diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 896a3d03f4a..a1af3159f2c 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -143,7 +143,8 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::begin static_assert(CanMemberBegin); - static_assert(CanMemberBegin == (input_range && is_reference_v>) ); + static_assert( + CanMemberBegin == (forward_range && is_reference_v>) ); if (forward_range) { const iterator_t i = r.begin(); if (!is_empty) { @@ -177,7 +178,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::end static_assert(CanMemberEnd); - static_assert(CanMemberEnd == (input_range && is_reference_v>) ); + static_assert(CanMemberEnd == (forward_range && is_reference_v>) ); // clang-format off static_assert(common_range == (forward_range && is_reference_v> && common_range && forward_range && common_range) ); From 42046d3edbf611e961c9b15065f2deb371e1a99d Mon Sep 17 00:00:00 2001 From: cpplearner Date: Mon, 13 Feb 2023 22:16:39 +0800 Subject: [PATCH 03/22] LWG-3698 (join_with_view part) --- stl/inc/ranges | 100 ++++++++++++++---- .../tests/P2441R2_views_join_with/test.cpp | 10 +- 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index dba648569aa..63b750c44a1 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3972,7 +3972,7 @@ namespace ranges { #ifdef __clang__ template // TRANSITION, LLVM-47414 concept _Can_const_join_with = - input_range && forward_range && is_reference_v>; + forward_range && forward_range && is_reference_v>; #endif // ^^^ workaround ^^^ _EXPORT_STD template @@ -4000,10 +4000,19 @@ namespace ranges { requires is_reference_v> class _Join_with_view_base<_Vw, _Pat> : public view_interface> {}; + template + class _Join_with_view_outer_iter_base : public _Join_with_view_base<_Vw, _Pat> { + protected: + _Non_propagating_cache> _Outer_it; + }; + + template + class _Join_with_view_outer_iter_base<_Vw, _Pat> : public _Join_with_view_base<_Vw, _Pat> {}; + _EXPORT_STD template requires view<_Vw> && input_range> && view<_Pat> && _Compatible_joinable_ranges, _Pat> - class join_with_view : public _Join_with_view_base<_Vw, _Pat> { + class join_with_view : public _Join_with_view_outer_iter_base<_Vw, _Pat> { private: template using _InnerRng = range_reference_t<_Maybe_const<_Const, _Vw>>; @@ -4040,10 +4049,26 @@ namespace ranges { }; template - class _Iterator : public _Category_base<_Const> { + class _Iterator_base : public _Category_base<_Const> {}; + + template + requires forward_range<_Vw> + class _Iterator_base<_Const> : public _Category_base<_Const> { + protected: + using _OuterIter = iterator_t<_Maybe_const<_Const, _Vw>>; + + _Iterator_base() = default; + constexpr explicit _Iterator_base(_OuterIter&& _Outer_it_) : _Outer_it{_STD move(_Outer_it_)} {} + + /* [[no_unique_address]] */ _OuterIter _Outer_it{}; + }; + + template + class _Iterator : public _Iterator_base<_Const> { private: friend join_with_view; + using _Mybase = _Iterator_base<_Const>; using _Parent_t = _Maybe_const<_Const, join_with_view>; using _Base = _Maybe_const<_Const, _Vw>; using _PatternBase = _Maybe_const<_Const, _Pat>; @@ -4056,29 +4081,55 @@ namespace ranges { static constexpr bool _Deref_is_glvalue = is_reference_v<_InnerRng<_Const>>; _Parent_t* _Parent{}; - /* [[no_unique_address]] */ _OuterIter _Outer_it{}; _Variantish<_PatternIter, _InnerIter> _Inner_it{}; - constexpr _Iterator(_Parent_t& _Parent_, iterator_t<_Base> _Outer_) - : _Parent{_STD addressof(_Parent_)}, _Outer_it{_STD move(_Outer_)} { - if (_Outer_it != _RANGES end(_Parent->_Range)) { + constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) + requires forward_range<_Base> + : _Mybase{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { + if (this->_Outer_it != _RANGES end(_Parent->_Range)) { + auto&& _Inner = _Update_inner(); + _Inner_it._Emplace_second(_RANGES begin(_Inner)); + _Satisfy(); + } + } + + constexpr _Iterator(_Parent_t& _Parent_) + requires (!forward_range<_Base>) + : _Parent{_STD addressof(_Parent_)} { + if (*_Parent->_Outer_it != _RANGES end(_Parent->_Range)) { auto&& _Inner = _Update_inner(); _Inner_it._Emplace_second(_RANGES begin(_Inner)); _Satisfy(); } } + _NODISCARD constexpr _OuterIter& _Get_outer() noexcept { + if constexpr (forward_range<_Base>) { + return this->_Outer_it; + } else { + return *_Parent->_Outer_it; + } + } + + _NODISCARD constexpr const _OuterIter& _Get_outer() const noexcept { + if constexpr (forward_range<_Base>) { + return this->_Outer_it; + } else { + return *_Parent->_Outer_it; + } + } + _NODISCARD constexpr auto&& _Update_inner() { if constexpr (_Deref_is_glvalue) { - return *_Outer_it; + return *_Get_outer(); } else { - return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Outer_it)._Val; + return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Get_outer())._Val; } } _NODISCARD constexpr auto&& _Get_inner() noexcept { if constexpr (_Deref_is_glvalue) { - return *_Outer_it; + return *_Get_outer(); } else { return (*_Parent->_Inner)._Val; } @@ -4112,8 +4163,8 @@ namespace ranges { break; } - ++_Outer_it; - if (_Outer_it == _RANGES end(_Parent->_Range)) { + ++_Get_outer(); + if (_Get_outer() == _RANGES end(_Parent->_Range)) { if constexpr (_Deref_is_glvalue) { _Inner_it._Emplace_first(); } @@ -4144,7 +4195,7 @@ namespace ranges { && convertible_to, _OuterIter> // && convertible_to>, _InnerIter> // && convertible_to, _PatternIter> // - : _Outer_it{_STD move(_It._Outer_it)}, _Parent{_It._Parent} { + : _Mybase{_STD move(_It._Outer_it)}, _Parent{_It._Parent} { switch (_It._Inner_it._Contains) { case _Variantish_state::_Holds_first: _Inner_it._Emplace_first(_STD move(_It._Inner_it._First)); @@ -4193,6 +4244,7 @@ namespace ranges { requires _Deref_is_glvalue && bidirectional_range<_Base> // && _Bidi_common_range<_InnerRng<_Const>> && _Bidi_common_range<_PatternBase> { + auto& _Outer_it = _Get_outer(); if (_Outer_it == _RANGES end(_Parent->_Range)) { --_Outer_it; auto&& _Inner = *_Outer_it; @@ -4245,7 +4297,7 @@ namespace ranges { } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) - requires _Deref_is_glvalue && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> + requires _Deref_is_glvalue && forward_range<_Base> && equality_comparable<_InnerIter> { if (_Left._Outer_it != _Right._Outer_it) { return false; @@ -4322,10 +4374,10 @@ namespace ranges { template _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const - noexcept(noexcept(_Fake_copy_init(_It._Outer_it == _Last))) { + noexcept(noexcept(_Fake_copy_init(_It._Get_outer() == _Last))) { _STL_INTERNAL_STATIC_ASSERT( sentinel_for, iterator_t<_Maybe_const<_OtherConst, _Vw>>>); - return _It._Outer_it == _Last; + return _It._Get_outer() == _Last; } public: @@ -4371,15 +4423,20 @@ namespace ranges { } _NODISCARD constexpr auto begin() { - constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng> && _Simple_view<_Pat>; - return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + if constexpr (forward_range<_Vw>) { + constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng> && _Simple_view<_Pat>; + return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + } else { + this->_Outer_it._Emplace(_RANGES begin(_Range)); + return _Iterator{*this}; + } } _NODISCARD constexpr auto begin() const #ifdef __clang__ // TRANSITION, LLVM-47414 requires _Can_const_join_with<_Vw, _Pat> #else // ^^^ workaround / no workaround vvv - requires input_range && forward_range && is_reference_v<_InnerRng> + requires forward_range && forward_range && is_reference_v<_InnerRng> #endif // TRANSITION, LLVM-47414 { return _Iterator{*this, _RANGES begin(_Range)}; @@ -4400,11 +4457,10 @@ namespace ranges { #ifdef __clang__ // TRANSITION, LLVM-47414 requires _Can_const_join_with<_Vw, _Pat> #else // ^^^ workaround / no workaround vvv - requires input_range && forward_range && is_reference_v<_InnerRng> + requires forward_range && forward_range && is_reference_v<_InnerRng> #endif // TRANSITION, LLVM-47414 { - if constexpr (forward_range && forward_range<_InnerRng> // - && common_range<_Vw> && common_range<_InnerRng>) { + if constexpr (forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { return _Iterator{*this, _RANGES end(_Range)}; } else { return _Sentinel{*this}; diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index d5b965a47cc..5278a4f2a05 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -186,8 +186,9 @@ constexpr void test_one(Outer&& rng, Delimiter&& delimiter, Expected&& expected) // Validate join_with_view::begin STATIC_ASSERT(CanMemberBegin); - STATIC_ASSERT(CanMemberBegin - == (input_range && forward_range && is_reference_v>) ); + STATIC_ASSERT( + CanMemberBegin + == (forward_range && forward_range && is_reference_v>) ); if (forward_range) { // intentionally not if constexpr const auto i = r.begin(); if (!is_empty) { @@ -221,8 +222,9 @@ constexpr void test_one(Outer&& rng, Delimiter&& delimiter, Expected&& expected) // Validate join_with_view::end static_assert(CanMemberEnd); - static_assert(CanMemberEnd - == (input_range && forward_range && is_reference_v>) ); + static_assert( + CanMemberEnd + == (forward_range && forward_range && is_reference_v>) ); static_assert(common_range == (forward_range && is_reference_v> && common_range && forward_range && common_range) ); From e17364758842cb17d682f8fdc439217e8d022505 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Mon, 13 Feb 2023 23:15:45 +0800 Subject: [PATCH 04/22] LWG-3698 (regex_iterator & regex_token_iterator part) --- stl/inc/regex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stl/inc/regex b/stl/inc/regex index 2b0e47f68e0..c6af9b2a248 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -2382,6 +2382,7 @@ public: using pointer = const value_type*; using reference = const value_type&; using iterator_category = forward_iterator_tag; + using iterator_concept = input_iterator_tag; regex_iterator() : _MyRe(nullptr) {} // construct end of sequence iterator @@ -2518,6 +2519,7 @@ public: using pointer = const value_type*; using reference = const value_type&; using iterator_category = forward_iterator_tag; + using iterator_concept = input_iterator_tag; regex_token_iterator() : _Res(nullptr) {} // construct end of sequence iterator From d3431f95d949c2933ce06e732bac1086bc990f48 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Tue, 14 Feb 2023 14:19:24 +0800 Subject: [PATCH 05/22] Add tests for LWG-3698 --- tests/std/tests/P0896R4_views_join/test.cpp | 38 +++++++++++++++++++ .../tests/P2441R2_views_join_with/test.cpp | 38 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index a1af3159f2c..8162ab9dece 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -502,6 +502,41 @@ void test_non_trivially_destructible_type() { // COMPILE-ONLY auto r2 = views::empty | views::transform([](Inner& r) { return r; }) | views::join; } +constexpr bool test_lwg3698() { + // LWG-3698 "regex_iterator and join_view don't work together very well" + struct stashing_iterator { + using difference_type = int; + using value_type = span; + + int x = 1; + + constexpr stashing_iterator& operator++() { + ++x; + return *this; + } + constexpr void operator++(int) { + ++x; + } + constexpr value_type operator*() const { + return {&x, &x + 1}; + } + constexpr bool operator==(default_sentinel_t) const { + return x > 3; + } + }; + + auto r = ranges::subrange{stashing_iterator{}, default_sentinel} | views::join; + auto r2 = r; + auto it = r.begin(); + auto it2 = r2.begin(); + + auto itcopy = it; + it = ++it2; + assert(*itcopy == 1); + + return true; +} + int main() { // Validate views constexpr string_view expected = "Hello World!"sv; @@ -594,4 +629,7 @@ int main() { STATIC_ASSERT(instantiation_test()); instantiation_test(); + + STATIC_ASSERT(test_lwg3698()); + assert(test_lwg3698()); } diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index 5278a4f2a05..c25e7c0f7ca 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -560,6 +560,41 @@ void test_valueless_iterator() { } } +constexpr bool test_lwg3698() { + // LWG-3698 "regex_iterator and join_view don't work together very well" + struct stashing_iterator { + using difference_type = int; + using value_type = span; + + int x = 1; + + constexpr stashing_iterator& operator++() { + ++x; + return *this; + } + constexpr void operator++(int) { + ++x; + } + constexpr value_type operator*() const { + return {&x, &x + 1}; + } + constexpr bool operator==(default_sentinel_t) const { + return x > 3; + } + }; + + auto r = ranges::subrange{stashing_iterator{}, default_sentinel} | views::join_with(views::empty); + auto r2 = r; + auto it = r.begin(); + auto it2 = r2.begin(); + + auto itcopy = it; + it = ++it2; + assert(*itcopy == 1); + + return true; +} + int main() { { auto filtered_and_joined = @@ -571,4 +606,7 @@ int main() { instantiation_test(); test_valueless_iterator(); + + STATIC_ASSERT(test_lwg3698()); + assert(test_lwg3698()); } From 02e58e5e1016facc78daca0424a93c23dc0a5cfb Mon Sep 17 00:00:00 2001 From: cpplearner Date: Mon, 13 Feb 2023 23:46:51 +0800 Subject: [PATCH 06/22] LWG-3700 The const begin of the join_view family does not require InnerRng to be a range --- stl/inc/ranges | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 63b750c44a1..5d34f3d5e96 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3556,7 +3556,8 @@ namespace ranges { #ifdef __clang__ template // TRANSITION, LLVM-47414 - concept _Can_const_join = forward_range && is_reference_v>; + concept _Can_const_join = forward_range && is_reference_v> + && input_range>; #endif // ^^^ workaround ^^^ template @@ -3912,7 +3913,7 @@ namespace ranges { #ifdef __clang__ // TRANSITION, LLVM-47414 requires _Can_const_join<_Vw> #else // ^^^ workaround / no workaround vvv - requires forward_range && is_reference_v<_InnerRng> + requires forward_range && is_reference_v<_InnerRng> && input_range<_InnerRng> #endif // TRANSITION, LLVM-47414 { return _Iterator{*this, _RANGES begin(_Range)}; @@ -3931,7 +3932,7 @@ namespace ranges { #ifdef __clang__ // TRANSITION, LLVM-47414 requires _Can_const_join<_Vw> #else // ^^^ workaround / no workaround vvv - requires forward_range && is_reference_v<_InnerRng> + requires forward_range && is_reference_v<_InnerRng> && input_range<_InnerRng> #endif // TRANSITION, LLVM-47414 { if constexpr (forward_range<_InnerRng> && common_range && common_range<_InnerRng>) { @@ -3972,7 +3973,8 @@ namespace ranges { #ifdef __clang__ template // TRANSITION, LLVM-47414 concept _Can_const_join_with = - forward_range && forward_range && is_reference_v>; + forward_range && forward_range && is_reference_v> + && input_range>; #endif // ^^^ workaround ^^^ _EXPORT_STD template @@ -4437,6 +4439,7 @@ namespace ranges { requires _Can_const_join_with<_Vw, _Pat> #else // ^^^ workaround / no workaround vvv requires forward_range && forward_range && is_reference_v<_InnerRng> + && input_range<_InnerRng> #endif // TRANSITION, LLVM-47414 { return _Iterator{*this, _RANGES begin(_Range)}; @@ -4458,6 +4461,7 @@ namespace ranges { requires _Can_const_join_with<_Vw, _Pat> #else // ^^^ workaround / no workaround vvv requires forward_range && forward_range && is_reference_v<_InnerRng> + && input_range<_InnerRng> #endif // TRANSITION, LLVM-47414 { if constexpr (forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { From a701dd13f93b57177fb133edf5bc224759fdf7e6 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Tue, 14 Feb 2023 14:41:45 +0800 Subject: [PATCH 07/22] Add tests for LWG-3700 --- tests/std/tests/P0896R4_views_join/test.cpp | 18 +++++++++++++--- .../tests/P2441R2_views_join_with/test.cpp | 21 +++++++++++++------ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 8162ab9dece..b7d7c98569b 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -143,8 +143,10 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::begin static_assert(CanMemberBegin); - static_assert( - CanMemberBegin == (forward_range && is_reference_v>) ); + // clang-format off + static_assert(CanMemberBegin == (forward_range && is_reference_v> + && input_range>) ); + // clang-format on if (forward_range) { const iterator_t i = r.begin(); if (!is_empty) { @@ -178,8 +180,9 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::end static_assert(CanMemberEnd); - static_assert(CanMemberEnd == (forward_range && is_reference_v>) ); // clang-format off + static_assert(CanMemberEnd == (forward_range && is_reference_v> + && input_range>) ); static_assert(common_range == (forward_range && is_reference_v> && common_range && forward_range && common_range) ); static_assert(common_range == (forward_range && is_reference_v> @@ -537,6 +540,15 @@ constexpr bool test_lwg3698() { return true; } +void test_lwg3700() { // COMPILE-ONLY + // LWG-3700 "The const begin of the join_view family does not require InnerRng to be a range" + auto r = views::iota(0, 5) | views::filter([](auto) { return true; }); + auto j = views::single(r) | views::join; + using J = decltype(j); + STATIC_ASSERT(!CanMemberBegin); + STATIC_ASSERT(!CanMemberEnd); +} + int main() { // Validate views constexpr string_view expected = "Hello World!"sv; diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index c25e7c0f7ca..b9a36a8435d 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -186,9 +186,9 @@ constexpr void test_one(Outer&& rng, Delimiter&& delimiter, Expected&& expected) // Validate join_with_view::begin STATIC_ASSERT(CanMemberBegin); - STATIC_ASSERT( - CanMemberBegin - == (forward_range && forward_range && is_reference_v>) ); + STATIC_ASSERT(CanMemberBegin + == (forward_range && forward_range && is_reference_v> + && input_range>) ); if (forward_range) { // intentionally not if constexpr const auto i = r.begin(); if (!is_empty) { @@ -222,9 +222,9 @@ constexpr void test_one(Outer&& rng, Delimiter&& delimiter, Expected&& expected) // Validate join_with_view::end static_assert(CanMemberEnd); - static_assert( - CanMemberEnd - == (forward_range && forward_range && is_reference_v>) ); + static_assert(CanMemberEnd + == (forward_range && forward_range && is_reference_v> + && input_range>) ); static_assert(common_range == (forward_range && is_reference_v> && common_range && forward_range && common_range) ); @@ -595,6 +595,15 @@ constexpr bool test_lwg3698() { return true; } +void test_lwg3700() { // COMPILE-ONLY + // LWG-3700 "The const begin of the join_view family does not require InnerRng to be a range" + auto r = views::iota(0, 5) | views::filter([](auto) { return true; }); + auto j = views::single(r) | views::join_with(-1); + using J = decltype(j); + STATIC_ASSERT(!CanMemberBegin); + STATIC_ASSERT(!CanMemberEnd); +} + int main() { { auto filtered_and_joined = From 3414a57fdc658782e487858bb3d93265f0c3afff Mon Sep 17 00:00:00 2001 From: cpplearner Date: Tue, 14 Feb 2023 00:00:44 +0800 Subject: [PATCH 08/22] LWG-3791 join_view::iterator::operator-- may be ill-formed --- stl/inc/ranges | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 5d34f3d5e96..a0d86582db2 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3560,6 +3560,11 @@ namespace ranges { && input_range>; #endif // ^^^ workaround ^^^ + template + _NODISCARD constexpr _Ty& _As_lvalue(_Ty&& _Val) noexcept { + return static_cast<_Ty&>(_Val); + } + template requires is_reference_v> class _Join_view_base<_Vw> : public view_interface> {}; @@ -3683,7 +3688,7 @@ namespace ranges { _STL_VERIFY(_Get_outer() != _RANGES end(_Parent->_Range), "cannot dereference join_view end iterator"); sentinel_t<_InnerRng<_Const>> _Last; if constexpr (_Deref_is_glvalue) { - _Last = _RANGES end(*_Get_outer()); + _Last = _RANGES end(_As_lvalue(*_Get_outer())); } else { _Last = _RANGES end((*_Parent->_Inner)._Val); } @@ -3751,7 +3756,7 @@ namespace ranges { constexpr _Iterator& operator++() { auto& _Outer = _Get_outer(); if constexpr (_Deref_is_glvalue) { - if (++*_Inner == _RANGES end(*_Outer)) { + if (++*_Inner == _RANGES end(_As_lvalue(*_Outer))) { ++_Outer; _Satisfy(); } @@ -3782,11 +3787,11 @@ namespace ranges { auto& _Outer = _Get_outer(); if (_Outer == _RANGES end(_Parent->_Range)) { --_Outer; - _Inner = _RANGES end(*_Outer); + _Inner = _RANGES end(_As_lvalue(*_Outer)); } - while (*_Inner == _RANGES begin(*_Outer)) { + while (*_Inner == _RANGES begin(_As_lvalue(*_Outer))) { --_Outer; - *_Inner = _RANGES end(*_Outer); + *_Inner = _RANGES end(_As_lvalue(*_Outer)); } --*_Inner; return *this; @@ -4089,8 +4094,7 @@ namespace ranges { requires forward_range<_Base> : _Mybase{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { if (this->_Outer_it != _RANGES end(_Parent->_Range)) { - auto&& _Inner = _Update_inner(); - _Inner_it._Emplace_second(_RANGES begin(_Inner)); + _Inner_it._Emplace_second(_RANGES begin(_Update_inner())); _Satisfy(); } } @@ -4099,8 +4103,7 @@ namespace ranges { requires (!forward_range<_Base>) : _Parent{_STD addressof(_Parent_)} { if (*_Parent->_Outer_it != _RANGES end(_Parent->_Range)) { - auto&& _Inner = _Update_inner(); - _Inner_it._Emplace_second(_RANGES begin(_Inner)); + _Inner_it._Emplace_second(_RANGES begin(_Update_inner())); _Satisfy(); } } @@ -4121,17 +4124,17 @@ namespace ranges { } } - _NODISCARD constexpr auto&& _Update_inner() { + _NODISCARD constexpr auto& _Update_inner() { if constexpr (_Deref_is_glvalue) { - return *_Get_outer(); + return _As_lvalue(*_Get_outer()); } else { return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Get_outer())._Val; } } - _NODISCARD constexpr auto&& _Get_inner() noexcept { + _NODISCARD constexpr auto& _Get_inner() noexcept { if constexpr (_Deref_is_glvalue) { - return *_Get_outer(); + return _As_lvalue(*_Get_outer()); } else { return (*_Parent->_Inner)._Val; } @@ -4155,13 +4158,11 @@ namespace ranges { break; } - auto&& _Inner = _Update_inner(); - _Inner_it._Emplace_second(_RANGES begin(_Inner)); + _Inner_it._Emplace_second(_RANGES begin(_Update_inner())); } else { _STL_INTERNAL_CHECK(_Inner_it._Contains == _Variantish_state::_Holds_second); - auto&& _Inner = _Get_inner(); - if (_Inner_it._Second != _RANGES end(_Inner)) { + if (_Inner_it._Second != _RANGES end(_Get_inner())) { break; } From fae90beb50295250df245b02d85241bd65150491 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Tue, 14 Feb 2023 15:10:13 +0800 Subject: [PATCH 09/22] Add ProxyRef::xvalue in range_algorithm_support.hpp --- tests/std/include/range_algorithm_support.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 36597c5160c..6587886650a 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -112,7 +112,7 @@ namespace test { enum class CanDifference : bool { no, yes }; enum class CanCompare : bool { no, yes }; - enum class ProxyRef { no, yes, prvalue }; + enum class ProxyRef { no, yes, prvalue, xvalue }; enum class WrappedState { wrapped, unwrapped, @@ -402,7 +402,8 @@ namespace test { static constexpr bool at_least = derived_from; using ReferenceType = conditional_t, - conditional_t, Element&>>; + conditional_t, + conditional_t>>; struct post_increment_proxy { Element* ptr_; @@ -444,7 +445,7 @@ namespace test { } [[nodiscard]] constexpr ReferenceType operator*() const noexcept { - return ReferenceType{*ptr_}; + return static_cast(*ptr_); } template From aae58b74e0e0d54f39fdd069539fbe539a276dfa Mon Sep 17 00:00:00 2001 From: cpplearner Date: Tue, 14 Feb 2023 15:59:14 +0800 Subject: [PATCH 10/22] Add test for LWG-3791 --- tests/std/tests/P0896R4_views_join/test.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index b7d7c98569b..9a40944c2fd 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -549,6 +549,17 @@ void test_lwg3700() { // COMPILE-ONLY STATIC_ASSERT(!CanMemberEnd); } +constexpr bool test_lwg3791() { + // LWG-3791 "join_view::iterator::operator-- may be ill-formed" + // Validate that join_view works when range_reference_t is an rvalue reference + using outer = test::range; + + instantiator::call(); + + return true; +} + int main() { // Validate views constexpr string_view expected = "Hello World!"sv; @@ -644,4 +655,7 @@ int main() { STATIC_ASSERT(test_lwg3698()); assert(test_lwg3698()); + + STATIC_ASSERT(test_lwg3791()); + assert(test_lwg3791()); } From 0553cc4a6bdc789447168583aff0279fed73fcf7 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Tue, 14 Feb 2023 18:36:12 +0800 Subject: [PATCH 11/22] Make join_view::iterator's constructors private --- stl/inc/ranges | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index a0d86582db2..98f317d7ca5 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3624,10 +3624,7 @@ namespace ranges { template class _Iterator : public _Iterator_base<_Const> { private: - template - friend class _Iterator; - template - friend class _Sentinel; + friend join_view; using _Mybase = _Iterator_base<_Const>; using _Parent_t = _Maybe_const<_Const, join_view>; @@ -3643,6 +3640,22 @@ namespace ranges { // an optional-like. _Parent_t* _Parent{}; + constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) + requires forward_range<_Base> + : _Mybase{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Adl_verify_range(this->_Outer, _RANGES end(_Parent_._Range)); + _Adl_verify_range(_RANGES begin(_Parent_._Range), this->_Outer); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Satisfy(); + } + + constexpr explicit _Iterator(_Parent_t& _Parent) + requires (!forward_range<_Base>) + : _Parent{_STD addressof(_Parent)} { + _Satisfy(); + } + _NODISCARD constexpr _OuterIter& _Get_outer() noexcept { if constexpr (forward_range<_Base>) { return this->_Outer; @@ -3715,22 +3728,6 @@ namespace ranges { _Iterator() requires default_initializable<_OuterIter> = default; // clang-format on - constexpr explicit _Iterator(_Parent_t& _Parent) - requires (!forward_range<_Base>) - : _Parent{_STD addressof(_Parent)} { - _Satisfy(); - } - - constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) - requires forward_range<_Base> - : _Mybase{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { -#if _ITERATOR_DEBUG_LEVEL != 0 - _Adl_verify_range(this->_Outer, _RANGES end(_Parent_._Range)); - _Adl_verify_range(_RANGES begin(_Parent_._Range), this->_Outer); -#endif // _ITERATOR_DEBUG_LEVEL != 0 - _Satisfy(); - } - constexpr _Iterator(_Iterator _It) requires _Const && convertible_to, _OuterIter> && convertible_to>, _InnerIter> From e93956ed24abd8c20dcbc6ef812902e47420a245 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Tue, 14 Feb 2023 19:40:31 +0800 Subject: [PATCH 12/22] Simplify join_with_view::iterator::operator-- --- stl/inc/ranges | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 98f317d7ca5..1dc135406e1 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -4247,8 +4247,7 @@ namespace ranges { auto& _Outer_it = _Get_outer(); if (_Outer_it == _RANGES end(_Parent->_Range)) { --_Outer_it; - auto&& _Inner = *_Outer_it; - _Inner_it._Emplace_second(_RANGES end(_Inner)); + _Inner_it._Emplace_second(_RANGES end(_Get_inner())); } for (;;) { @@ -4256,15 +4255,13 @@ namespace ranges { auto& _It = _Inner_it._First; if (_It == _RANGES begin(_Parent->_Pattern)) { --_Outer_it; - auto&& _Inner = *_Outer_it; - _Inner_it._Emplace_second(_RANGES end(_Inner)); + _Inner_it._Emplace_second(_RANGES end(_Get_inner())); } else { break; } } else if (_Inner_it._Contains == _Variantish_state::_Holds_second) { - auto& _It = _Inner_it._Second; - auto&& _Inner = *_Outer_it; - if (_It == _RANGES begin(_Inner)) { + auto& _It = _Inner_it._Second; + if (_It == _RANGES begin(_Get_inner())) { _Inner_it._Emplace_first(_RANGES end(_Parent->_Pattern)); } else { break; From ae4236e67562f72b48ddf1b60691120296b92ed6 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Tue, 14 Feb 2023 21:35:34 +0800 Subject: [PATCH 13/22] Update libc++ skips --- tests/libcxx/expected_results.txt | 9 +++++++++ tests/libcxx/skipped_tests.txt | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index bb5eda0dd71..0a1d9f43774 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -138,6 +138,15 @@ std/ranges/range.access/rbegin.pass.cpp FAIL std/ranges/range.access/rend.pass.cpp FAIL std/ranges/range.access/size.pass.cpp FAIL +# libc++ doesn't implement P2770R0 "Stashing stashing iterators for proper flattening" +std/ranges/range.adaptors/range.join.view/end.pass.cpp FAIL +std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp FAIL +std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp FAIL +std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp FAIL +std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp FAIL +std/re/re.iter/re.regiter/iterator_concept_conformance.compile.pass.cpp FAIL +std/re/re.iter/re.tokiter/iterator_concept_conformance.compile.pass.cpp FAIL + # *** INTERACTIONS WITH CONTEST / C1XX THAT UPSTREAM LIKELY WON'T FIX *** # Tracked by VSO-593630 " Enable libcxx filesystem tests" diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index f7cd2590acc..2422e35c44f 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -138,6 +138,15 @@ ranges\range.access\rbegin.pass.cpp ranges\range.access\rend.pass.cpp ranges\range.access\size.pass.cpp +# libc++ doesn't implement P2770R0 "Stashing stashing iterators for proper flattening" +ranges\range.adaptors\range.join.view\end.pass.cpp +ranges\range.adaptors\range.join.view\iterator\ctor.other.pass.cpp +ranges\range.adaptors\range.join.view\iterator\ctor.parent.outer.pass.cpp +ranges\range.adaptors\range.join.view\iterator\eq.pass.cpp +ranges\range.adaptors\range.join.view\sentinel\eq.pass.cpp +re\re.iter\re.regiter\iterator_concept_conformance.compile.pass.cpp +re\re.iter\re.tokiter\iterator_concept_conformance.compile.pass.cpp + # *** INTERACTIONS WITH CONTEST / C1XX THAT UPSTREAM LIKELY WON'T FIX *** # Tracked by VSO-593630 " Enable libcxx filesystem tests" From 57cfc02c0f37b8ac8dd799c8c78f6d4f8d7de6c6 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Wed, 15 Feb 2023 18:44:11 +0800 Subject: [PATCH 14/22] Add iterator_concept only if _HAS_CXX20 --- stl/inc/regex | 8 ++++++-- stl/inc/yvals_core.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/stl/inc/regex b/stl/inc/regex index c6af9b2a248..35899c2aa22 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -2382,7 +2382,9 @@ public: using pointer = const value_type*; using reference = const value_type&; using iterator_category = forward_iterator_tag; - using iterator_concept = input_iterator_tag; +#if _HAS_CXX20 + using iterator_concept = input_iterator_tag; +#endif // _HAS_CXX20 regex_iterator() : _MyRe(nullptr) {} // construct end of sequence iterator @@ -2519,7 +2521,9 @@ public: using pointer = const value_type*; using reference = const value_type&; using iterator_category = forward_iterator_tag; - using iterator_concept = input_iterator_tag; +#if _HAS_CXX20 + using iterator_concept = input_iterator_tag; +#endif // _HAS_CXX20 regex_token_iterator() : _Res(nullptr) {} // construct end of sequence iterator diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index ce8d39dcec3..c376259daad 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -287,6 +287,7 @@ // P2520R0 move_iterator Should Be A Random-Access Iterator // P2588R3 barrier's Phase Completion Guarantees // P2711R1 Making Multi-Param Constructors Of Views explicit +// P2770R0 Stashing Stashing Iterators For Proper Flattening // _HAS_CXX20 indirectly controls: // P0619R4 Removing C++17-Deprecated Features From 9343d0c1eb21120f89dbc14fbb9e21caff091988 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Thu, 16 Feb 2023 18:03:58 +0800 Subject: [PATCH 15/22] Fix LWG-3791 test --- tests/std/tests/P0896R4_views_join/test.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 9a40944c2fd..ad9c9601c00 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -552,10 +552,11 @@ void test_lwg3700() { // COMPILE-ONLY constexpr bool test_lwg3791() { // LWG-3791 "join_view::iterator::operator-- may be ill-formed" // Validate that join_view works when range_reference_t is an rvalue reference - using outer = test::range; + using inner = test::range; + using outer = test::range; - instantiator::call(); + instantiator::call(); return true; } From 06816464a713f2f867c5baac99d5f629f7b22a60 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Fri, 17 Feb 2023 15:38:19 +0800 Subject: [PATCH 16/22] Address review comments from StephanTLavavej --- stl/inc/ranges | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 1dc135406e1..349a0c47d39 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3609,7 +3609,7 @@ namespace ranges { : public _Category_base<_Maybe_const<_Const, _Vw>, _InnerRng<_Const>, is_reference_v<_InnerRng<_Const>>> {}; template - requires forward_range<_Vw> + requires forward_range<_Maybe_const<_Const, _Vw>> class _Iterator_base<_Const> : public _Category_base<_Maybe_const<_Const, _Vw>, _InnerRng<_Const>, is_reference_v<_InnerRng<_Const>>> { protected: @@ -3840,10 +3840,7 @@ namespace ranges { template class _Sentinel { private: - template - friend class _Iterator; - template - friend class _Sentinel; + friend join_view; using _Parent_t = _Maybe_const<_Const, join_view>; using _Base = _Maybe_const<_Const, _Vw>; @@ -4056,7 +4053,7 @@ namespace ranges { class _Iterator_base : public _Category_base<_Const> {}; template - requires forward_range<_Vw> + requires forward_range<_Maybe_const<_Const, _Vw>> class _Iterator_base<_Const> : public _Category_base<_Const> { protected: using _OuterIter = iterator_t<_Maybe_const<_Const, _Vw>>; @@ -4096,7 +4093,7 @@ namespace ranges { } } - constexpr _Iterator(_Parent_t& _Parent_) + constexpr explicit _Iterator(_Parent_t& _Parent_) requires (!forward_range<_Base>) : _Parent{_STD addressof(_Parent_)} { if (*_Parent->_Outer_it != _RANGES end(_Parent->_Range)) { @@ -4163,8 +4160,9 @@ namespace ranges { break; } - ++_Get_outer(); - if (_Get_outer() == _RANGES end(_Parent->_Range)) { + auto& _Outer_it = _Get_outer(); + ++_Outer_it; + if (_Outer_it == _RANGES end(_Parent->_Range)) { if constexpr (_Deref_is_glvalue) { _Inner_it._Emplace_first(); } From ba47b18e74e29f68220e08cf5faebb5d52c0460c Mon Sep 17 00:00:00 2001 From: cpplearner Date: Fri, 17 Feb 2023 16:39:49 +0800 Subject: [PATCH 17/22] Add tests for ranges that do not model simple-view --- tests/std/tests/P0896R4_views_join/test.cpp | 21 +++++++++++++++++++ .../tests/P2441R2_views_join_with/test.cpp | 21 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index ad9c9601c00..8ecfc1b9c90 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -537,6 +537,27 @@ constexpr bool test_lwg3698() { it = ++it2; assert(*itcopy == 1); + struct intricate_range { + constexpr stashing_iterator begin() { + return {}; + } + constexpr default_sentinel_t end() { + return {}; + } + constexpr const span* begin() const { + return ranges::begin(intervals); + } + constexpr const span* end() const { + return ranges::end(intervals); + } + }; + + auto jv = intricate_range{} | views::join(); + auto cit = as_const(jv).begin(); + assert(*++cit == 1); + assert(*--cit == 0); + assert(ranges::equal(as_const(jv), expected_ints)); + return true; } diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index b9a36a8435d..dade957f5e7 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -592,6 +592,27 @@ constexpr bool test_lwg3698() { it = ++it2; assert(*itcopy == 1); + struct intricate_range { + constexpr stashing_iterator begin() { + return {}; + } + constexpr default_sentinel_t end() { + return {}; + } + constexpr const span* begin() const { + return ranges::begin(intervals); + } + constexpr const span* end() const { + return ranges::end(intervals); + } + }; + + auto jv = intricate_range{} | views::join_with(-1); + auto cit = as_const(jv).begin(); + assert(*++cit == 1); + assert(*--cit == 0); + assert(ranges::equal(as_const(jv), expected_ints)); + return true; } From f402d2961dfb39d8e92ee8673a337b92d5923250 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Fri, 17 Feb 2023 16:56:41 +0800 Subject: [PATCH 18/22] Remove redundant forward declarations of _Sentinel --- stl/inc/ranges | 6 ------ 1 file changed, 6 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 349a0c47d39..956af978905 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3586,9 +3586,6 @@ namespace ranges { using _InnerRng = range_reference_t<_Maybe_const<_Const, _Vw>>; /* [[no_unique_address]] */ _Vw _Range{}; - template - class _Sentinel; - template struct _Category_base {}; @@ -4021,9 +4018,6 @@ namespace ranges { /* [[no_unique_address]] */ _Vw _Range{}; /* [[no_unique_address]] */ _Pat _Pattern{}; - template - class _Sentinel; - template struct _Category_base {}; From 1f68bbac3fbee0b5f4886ab7025d64d3461a87a6 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Fri, 17 Feb 2023 17:20:25 +0800 Subject: [PATCH 19/22] Fix test --- tests/std/tests/P0896R4_views_join/test.cpp | 2 +- tests/std/tests/P2441R2_views_join_with/test.cpp | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 8ecfc1b9c90..f0f3e600734 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -552,7 +552,7 @@ constexpr bool test_lwg3698() { } }; - auto jv = intricate_range{} | views::join(); + auto jv = intricate_range{} | views::join; auto cit = as_const(jv).begin(); assert(*++cit == 1); assert(*--cit == 0); diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index dade957f5e7..74464007590 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -592,7 +592,11 @@ constexpr bool test_lwg3698() { it = ++it2; assert(*itcopy == 1); + int expected_ints[] = {1, 2, 3, 5, 7}; + struct intricate_range { + span intervals[2] = {{expected_ints + 0, expected_ints + 3}, {expected_ints + 3, expected_ints + 5}}; + constexpr stashing_iterator begin() { return {}; } @@ -607,10 +611,10 @@ constexpr bool test_lwg3698() { } }; - auto jv = intricate_range{} | views::join_with(-1); + auto jv = intricate_range{} | views::join_with(views::empty); auto cit = as_const(jv).begin(); - assert(*++cit == 1); - assert(*--cit == 0); + assert(*++cit == 2); + assert(*--cit == 1); assert(ranges::equal(as_const(jv), expected_ints)); return true; From acf35921ffa409ee0dfff3122fc79576a45e5e51 Mon Sep 17 00:00:00 2001 From: cpplearner Date: Sat, 18 Feb 2023 16:12:39 +0800 Subject: [PATCH 20/22] Fix test --- tests/std/tests/P2441R2_views_join_with/test.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index 74464007590..062449df435 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -592,10 +592,11 @@ constexpr bool test_lwg3698() { it = ++it2; assert(*itcopy == 1); - int expected_ints[] = {1, 2, 3, 5, 7}; + int expected_ints[] = {1, 2, 3, 5, 7}; + span intervals[2] = {{expected_ints + 0, expected_ints + 3}, {expected_ints + 3, expected_ints + 5}}; struct intricate_range { - span intervals[2] = {{expected_ints + 0, expected_ints + 3}, {expected_ints + 3, expected_ints + 5}}; + span* p; constexpr stashing_iterator begin() { return {}; @@ -604,14 +605,14 @@ constexpr bool test_lwg3698() { return {}; } constexpr const span* begin() const { - return ranges::begin(intervals); + return p; } constexpr const span* end() const { - return ranges::end(intervals); + return p + 2; } }; - auto jv = intricate_range{} | views::join_with(views::empty); + auto jv = intricate_range{.p = intervals} | views::join_with(views::empty); auto cit = as_const(jv).begin(); assert(*++cit == 2); assert(*--cit == 1); From 63a7ffb745863a14844d6c888b3deb19cb6bc334 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 22 Feb 2023 10:50:56 -0800 Subject: [PATCH 21/22] Code review feedback. --- tests/std/tests/P2441R2_views_join_with/test.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index 062449df435..a5e634f2d8e 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -592,8 +592,8 @@ constexpr bool test_lwg3698() { it = ++it2; assert(*itcopy == 1); - int expected_ints[] = {1, 2, 3, 5, 7}; - span intervals[2] = {{expected_ints + 0, expected_ints + 3}, {expected_ints + 3, expected_ints + 5}}; + constexpr int expected_ints[] = {1, 2, 3, 5, 7}; + span intervals[2] = {{expected_ints + 0, expected_ints + 3}, {expected_ints + 3, expected_ints + 5}}; struct intricate_range { span* p; @@ -612,11 +612,11 @@ constexpr bool test_lwg3698() { } }; - auto jv = intricate_range{.p = intervals} | views::join_with(views::empty); - auto cit = as_const(jv).begin(); + auto jwv = intricate_range{.p = intervals} | views::join_with(views::empty); + auto cit = as_const(jwv).begin(); assert(*++cit == 2); assert(*--cit == 1); - assert(ranges::equal(as_const(jv), expected_ints)); + assert(ranges::equal(as_const(jwv), expected_ints)); return true; } From 6ba7036243e756b2a3c4b2294bbff94afabcafc4 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sun, 26 Feb 2023 12:32:46 -0800 Subject: [PATCH 22/22] NON-TRIVIAL merge conflict resolutions! GH 3493 ": Fix misused list-initializations" added test coverage for GH 3014 ": list-initialization is misused". This defined an input_iterator named init_list_not_constructible_iterator. Now, P2770R0 "Stashing Stashing Iterators For Proper Flattening" is upgrading join_view and join_with_view's const overload of begin() to require forward_range. Therefore, we need to upgrade init_list_not_constructible_iterator to be a forward_iterator: * Mark its iterator_category as forward_iterator_tag. * Make it equality comparable, required for forward iterators. * Verify that it is now a forward_iterator. * Rename usage from InRange to FwdRange. Finally, this found two more improper uses of braces in , within join_view::_Iterator_base and join_with_view::_Iterator_base. --- stl/inc/ranges | 4 ++-- tests/std/include/range_algorithm_support.hpp | 8 +++++--- tests/std/tests/P0896R4_views_elements/test.cpp | 4 ++-- tests/std/tests/P0896R4_views_join/test.cpp | 4 ++-- tests/std/tests/P0896R4_views_transform/test.cpp | 4 ++-- tests/std/tests/P2441R2_views_join_with/test.cpp | 4 ++-- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 3f4135e987b..edcb62f86bb 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3624,7 +3624,7 @@ namespace ranges { using _OuterIter = iterator_t<_Maybe_const<_Const, _Vw>>; _Iterator_base() = default; - constexpr explicit _Iterator_base(_OuterIter&& _Outer_) : _Outer{_STD move(_Outer_)} {} + constexpr explicit _Iterator_base(_OuterIter&& _Outer_) : _Outer(_STD move(_Outer_)) {} /* [[no_unique_address]] */ _OuterIter _Outer{}; }; @@ -4064,7 +4064,7 @@ namespace ranges { using _OuterIter = iterator_t<_Maybe_const<_Const, _Vw>>; _Iterator_base() = default; - constexpr explicit _Iterator_base(_OuterIter&& _Outer_it_) : _Outer_it{_STD move(_Outer_it_)} {} + constexpr explicit _Iterator_base(_OuterIter&& _Outer_it_) : _Outer_it(_STD move(_Outer_it_)) {} /* [[no_unique_address]] */ _OuterIter _Outer_it{}; }; diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index b57c989a999..95f2b940114 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -394,8 +394,9 @@ namespace test { template struct init_list_not_constructible_iterator { - using difference_type = int; - using value_type = T; + using iterator_category = std::forward_iterator_tag; + using difference_type = int; + using value_type = T; init_list_not_constructible_iterator() = default; init_list_not_constructible_iterator(T*) {} @@ -407,10 +408,11 @@ namespace test { init_list_not_constructible_iterator& operator++(); // not defined init_list_not_constructible_iterator operator++(int); // not defined + bool operator==(init_list_not_constructible_iterator) const; // not defined bool operator==(init_list_not_constructible_sentinel) const; // not defined }; - static_assert(std::input_iterator>); + static_assert(std::forward_iterator>); static_assert( std::sentinel_for, init_list_not_constructible_iterator>); diff --git a/tests/std/tests/P0896R4_views_elements/test.cpp b/tests/std/tests/P0896R4_views_elements/test.cpp index 1bff24a240a..61a76c174f7 100644 --- a/tests/std/tests/P0896R4_views_elements/test.cpp +++ b/tests/std/tests/P0896R4_views_elements/test.cpp @@ -396,7 +396,7 @@ constexpr void instantiation_test() { // GH-3014 ": list-initialization is misused" void test_gh_3014() { // COMPILE-ONLY - struct InRange { + struct FwdRange { P* begin() { return nullptr; } @@ -410,7 +410,7 @@ void test_gh_3014() { // COMPILE-ONLY } }; - auto r = InRange{} | views::elements<0>; + auto r = FwdRange{} | views::elements<0>; [[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator i)' } diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index ba75b46af5c..96d87e350f9 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -514,7 +514,7 @@ void test_non_trivially_destructible_type() { // COMPILE-ONLY // GH-3014 ": list-initialization is misused" void test_gh_3014() { // COMPILE-ONLY - struct InRange { + struct FwdRange { string* begin() { return nullptr; } @@ -528,7 +528,7 @@ void test_gh_3014() { // COMPILE-ONLY } }; - auto r = InRange{} | views::join; + auto r = FwdRange{} | views::join; [[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator i)' } diff --git a/tests/std/tests/P0896R4_views_transform/test.cpp b/tests/std/tests/P0896R4_views_transform/test.cpp index e314e7e6a33..57bfc5020cd 100644 --- a/tests/std/tests/P0896R4_views_transform/test.cpp +++ b/tests/std/tests/P0896R4_views_transform/test.cpp @@ -813,7 +813,7 @@ void test_gh_1709() { // GH-3014 ": list-initialization is misused" void test_gh_3014() { // COMPILE-ONLY - struct InRange { + struct FwdRange { int* begin() { return nullptr; } @@ -827,7 +827,7 @@ void test_gh_3014() { // COMPILE-ONLY } }; - auto r = InRange{} | views::transform(identity{}); + auto r = FwdRange{} | views::transform(identity{}); [[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator i)' } diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index 6321b43f8e8..a1e61bf65a0 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -572,7 +572,7 @@ struct FakeStr { }; void test_gh_3014() { // COMPILE-ONLY - struct InRange { + struct FwdRange { FakeStr* begin() { return nullptr; } @@ -590,7 +590,7 @@ void test_gh_3014() { // COMPILE-ONLY } }; - auto r = InRange{} | views::join_with('-'); + auto r = FwdRange{} | views::join_with('-'); [[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator i)' [[maybe_unused]] decltype(as_const(r).end()) s = r.end(); // Check 'sentinel(sentinel s)' }