From 9f6430d6d95d81672f0baf72d78ac17386c59b0d Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Mon, 23 Jan 2023 18:06:29 +0800 Subject: [PATCH 1/6] Implement LWG-3798 For: - `cpp17-forward-iterator` - `transform_view` - `join_with_view` --- stl/inc/__msvc_iter_core.hpp | 2 +- stl/inc/ranges | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stl/inc/__msvc_iter_core.hpp b/stl/inc/__msvc_iter_core.hpp index 96efb43363..aeacf38ca3 100644 --- a/stl/inc/__msvc_iter_core.hpp +++ b/stl/inc/__msvc_iter_core.hpp @@ -310,7 +310,7 @@ struct _Iter_traits_category2 { // clang-format off template -concept _Cpp17_forward_delta = constructible_from<_It> && is_lvalue_reference_v> +concept _Cpp17_forward_delta = constructible_from<_It> && is_reference_v> && same_as>, typename indirectly_readable_traits<_It>::value_type> && requires(_It __i) { { __i++ } -> convertible_to; diff --git a/stl/inc/ranges b/stl/inc/ranges index 19f422d78f..45790d2767 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2091,12 +2091,12 @@ namespace ranges { template requires forward_range<_Maybe_const<_Const, _Vw>> struct _Category_base<_Const> { - using _Base = _Maybe_const<_Const, _Vw>; - using iterator_category = conditional_t< - is_lvalue_reference_v&, range_reference_t<_Base>>>, - conditional_t>, contiguous_iterator_tag>, - random_access_iterator_tag, _Iter_cat_t>>, - input_iterator_tag>; + using _Base = _Maybe_const<_Const, _Vw>; + using iterator_category = + conditional_t&, range_reference_t<_Base>>>, + conditional_t>, contiguous_iterator_tag>, + random_access_iterator_tag, _Iter_cat_t>>, + input_iterator_tag>; }; template @@ -3716,7 +3716,7 @@ namespace ranges { using _PatternBase = _Maybe_const<_Const, _Pat>; using iterator_category = conditional_t< - !is_lvalue_reference_v, range_reference_t<_PatternBase>>>, + !is_reference_v, range_reference_t<_PatternBase>>>, input_iterator_tag, conditional_t && common_range<_PatternBase> && derived_from<_Iter_cat_t>, bidirectional_iterator_tag> From 2f9ba470579671ec81ef54a4dc2022cf5ddd759a Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Mon, 23 Jan 2023 23:50:12 +0800 Subject: [PATCH 2/6] Test coverage for LWG-3798 For `cpp17-forward-iterator`, `transform_view`, and `join_with_view`. --- .../test.cpp | 69 +++++++++++++++++++ .../tests/P0896R4_views_transform/test.cpp | 34 +++++++++ .../tests/P2441R2_views_join_with/test.cpp | 59 ++++++++++++++++ 3 files changed, 162 insertions(+) diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index 60f3a66777..654e687a75 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -204,6 +204,56 @@ struct std::indirectly_readable_traits> { using value_type = double; }; +// Validate iterator_category of iterators whose reference types are rvalue references (LWG-3798). +struct xvalue_forward_iter { + using value_type = double; + using difference_type = long; + + value_type&& operator*() const; + xvalue_forward_iter& operator++(); + xvalue_forward_iter operator++(int); + + bool operator==(xvalue_forward_iter const&) const = default; +}; + +struct xvalue_bidi_iter { + using value_type = double; + using difference_type = long; + using reference = value_type&&; + + value_type&& operator*() const; + xvalue_bidi_iter& operator++(); + xvalue_bidi_iter operator++(int); + + bool operator==(xvalue_bidi_iter const&) const = default; + + xvalue_bidi_iter& operator--(); + xvalue_bidi_iter operator--(int); +}; + +struct xvalue_random_iter { + using value_type = double; + using D = long; + + value_type&& operator*() const; + xvalue_random_iter& operator++(); + xvalue_random_iter operator++(int); + + xvalue_random_iter& operator--(); + xvalue_random_iter operator--(int); + + bool operator==(xvalue_random_iter const&) const; + std::strong_ordering operator<=>(xvalue_random_iter const&) const; + + value_type&& operator[](D) const; + xvalue_random_iter& operator-=(D); + xvalue_random_iter operator-(D) const; + D operator-(xvalue_random_iter const&) const; + xvalue_random_iter& operator+=(D); + xvalue_random_iter operator+(D) const; + friend xvalue_random_iter operator+(D, const xvalue_random_iter&); +}; + template struct proxy_iterator { using difference_type = int; @@ -880,6 +930,7 @@ namespace iterator_traits_test { // * 3.2.3.3 "... Otherwise, iterator_category names... forward_iterator_tag if I satisfies cpp17-forward-iterator." STATIC_ASSERT( check, no_such_type, forward_iterator_tag, double, long, void, double const&>()); + STATIC_ASSERT(check()); // N4820 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval().operator->()) is well-formed, then // pointer names that type." STATIC_ASSERT(check>, no_such_type, forward_iterator_tag, double, @@ -898,6 +949,7 @@ namespace iterator_traits_test { // cpp17-bidirectional-iterator." STATIC_ASSERT( check, no_such_type, bidirectional_iterator_tag, double, long, void, double const&>()); + STATIC_ASSERT(check()); // N4820 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval().operator->()) is well-formed, then // pointer names that type." STATIC_ASSERT(check>, no_such_type, bidirectional_iterator_tag, double, @@ -918,6 +970,7 @@ namespace iterator_traits_test { // cpp17-random-access-iterator." STATIC_ASSERT( check, no_such_type, random_access_iterator_tag, double, long, void, double const&>()); + STATIC_ASSERT(check()); STATIC_ASSERT( check, no_such_type, random_access_iterator_tag, double, long, void, double const&>()); // N4820 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval().operator->()) is well-formed, then @@ -3092,6 +3145,10 @@ namespace reverse_iterator_test { STATIC_ASSERT(same_as>::iterator_category, random_access_iterator_tag>); STATIC_ASSERT(same_as>::iterator_concept, bidirectional_iterator_tag>); STATIC_ASSERT(same_as>::iterator_category, bidirectional_iterator_tag>); + STATIC_ASSERT(same_as::iterator_concept, random_access_iterator_tag>); + STATIC_ASSERT(same_as::iterator_category, random_access_iterator_tag>); + STATIC_ASSERT(same_as::iterator_concept, bidirectional_iterator_tag>); + STATIC_ASSERT(same_as::iterator_category, bidirectional_iterator_tag>); // Validate operator-> for a pointer, and for non-pointers with and without operator->() // clang-format off @@ -3285,6 +3342,12 @@ namespace move_iterator_test { STATIC_ASSERT(same_as::iterator_concept, input_iterator_tag>); STATIC_ASSERT(same_as::iterator_category, input_iterator_tag>); STATIC_ASSERT(same_as>::iterator_concept, input_iterator_tag>); + STATIC_ASSERT(same_as::iterator_concept, random_access_iterator_tag>); + STATIC_ASSERT(same_as::iterator_category, random_access_iterator_tag>); + STATIC_ASSERT(same_as::iterator_concept, bidirectional_iterator_tag>); + STATIC_ASSERT(same_as::iterator_category, bidirectional_iterator_tag>); + STATIC_ASSERT(same_as::iterator_concept, forward_iterator_tag>); + STATIC_ASSERT(same_as::iterator_category, forward_iterator_tag>); STATIC_ASSERT(!has_member_iter_category>>); STATIC_ASSERT(same_as>::iterator_concept, input_iterator_tag>); STATIC_ASSERT(!has_member_iter_category>>); @@ -3448,6 +3511,12 @@ namespace counted_iterator_test { STATIC_ASSERT( same_as>>::iterator_category, forward_iterator_tag>); STATIC_ASSERT(same_as>::iterator_category, input_iterator_tag>); + STATIC_ASSERT( + same_as>::iterator_category, random_access_iterator_tag>); + STATIC_ASSERT( + same_as>::iterator_category, bidirectional_iterator_tag>); + STATIC_ASSERT( + same_as>::iterator_category, forward_iterator_tag>); // Validate postincrement STATIC_ASSERT(same_as&>()++), simple_input_iter>); diff --git a/tests/std/tests/P0896R4_views_transform/test.cpp b/tests/std/tests/P0896R4_views_transform/test.cpp index bb7ec19ba7..e0a219e333 100644 --- a/tests/std/tests/P0896R4_views_transform/test.cpp +++ b/tests/std/tests/P0896R4_views_transform/test.cpp @@ -363,6 +363,37 @@ constexpr void test_difference_on_const_functor(Rng&& rng) { } } +// Test xvalue ranges (LWG-3798) +struct move_fn { + constexpr auto&& operator()(auto&& x) const noexcept { + return move(x); + } +}; + +template +constexpr void test_xvalue_ranges(Rng&& rng) { + using ranges::transform_view, ranges::input_range, ranges::forward_range, ranges::bidirectional_range, + ranges::random_access_range, ranges::iterator_t, ranges::range_reference_t, ranges::range_value_t; + + using V = views::all_t; + using TV = transform_view; + + auto r = forward(rng) | views::transform(move_fn{}); + STATIC_ASSERT(is_same_v); + + STATIC_ASSERT(is_rvalue_reference_v>); + + if constexpr (forward_range) { + using It = iterator_t; + using TVIt = iterator_t; + using VItCat = typename iterator_traits::iterator_category; + using TVItCat = typename iterator_traits::iterator_category; + STATIC_ASSERT( + is_same_v + || (is_same_v && is_same_v) ); + } +} + static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; static constexpr int transformed_ints[] = {8, 9, 10, 11, 12, 13, 14, 15}; @@ -374,6 +405,9 @@ struct instantiator { R r2{some_ints}; test_difference_on_const_functor(r2); + + R r3{some_ints}; + test_xvalue_ranges(r3); } }; diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index 0233e7a5b0..102574afc3 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -55,6 +55,55 @@ constexpr void test_one(Outer&& rng, Delimiter&& delimiter, Expected&& expected) && common_range && bidirectional_range && common_range) ); STATIC_ASSERT(!ranges::random_access_range); + // Validate iterator_category + if constexpr (forward_range) { + using OuterIter = iterator_t; + using InnerIter = iterator_t>; + using PatternIter = iterator_t; + using OuterCat = typename iterator_traits::iterator_category; + using InnerCat = typename iterator_traits::iterator_category; + using PatternCat = typename iterator_traits::iterator_category; + + if constexpr (!is_reference_v, iter_reference_t>>) { + STATIC_ASSERT(same_as::iterator_category, input_iterator_tag>); + } else if constexpr (derived_from + && derived_from + && derived_from + && common_range> && common_range) { + STATIC_ASSERT(same_as::iterator_category, bidirectional_iterator_tag>); + } else if constexpr (derived_from + && derived_from + && derived_from) { + STATIC_ASSERT(same_as::iterator_category, forward_iterator_tag>); + } else { + STATIC_ASSERT(same_as::iterator_category, input_iterator_tag>); + } + } + + if constexpr (forward_range) { + using OuterIter = iterator_t; + using InnerIter = iterator_t>; + using PatternIter = iterator_t; + using OuterCat = typename iterator_traits::iterator_category; + using InnerCat = typename iterator_traits::iterator_category; + using PatternCat = typename iterator_traits::iterator_category; + + if constexpr (!is_reference_v, iter_reference_t>>) { + STATIC_ASSERT(same_as::iterator_category, input_iterator_tag>); + } else if constexpr (derived_from + && derived_from + && derived_from + && common_range> && common_range) { + STATIC_ASSERT(same_as::iterator_category, bidirectional_iterator_tag>); + } else if constexpr (derived_from + && derived_from + && derived_from) { + STATIC_ASSERT(same_as::iterator_category, forward_iterator_tag>); + } else { + STATIC_ASSERT(same_as::iterator_category, input_iterator_tag>); + } + } + // Validate range adaptor object and range adaptor closure constexpr bool is_view = ranges::view>; const auto closure = views::join_with(delimiter); @@ -314,6 +363,16 @@ struct instantiator { Outer empty{span{}}; test_one(empty, "*#"sv, views::empty); } + { // Range-of-rvalue delimiter + Inner inner_ranges[] = {Inner{span{input[0]}}, Inner{span{input[1]}}, Inner{span{input[2]}}, + Inner{span{input[3]}}, Inner{span{input[4]}}, Inner{span{input[5]}}, Inner{span{input[6]}}, + Inner{span{input[7]}}}; + Outer r{inner_ranges}; + test_one(r | views::as_rvalue, "*#"sv | views::as_rvalue, expected_range); + + Outer empty{span{}}; + test_one(empty | views::as_rvalue, "*#"sv | views::as_rvalue, views::empty); + } } }; From 92b95bcbdde269c475948d313c4e5c3b7c48983b Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Mon, 23 Jan 2023 23:50:53 +0800 Subject: [PATCH 3/6] Update references to WD to WG21-N4928 --- stl/inc/ranges | 10 ++-- .../test.cpp | 46 +++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 45790d2767..c0ec9322c8 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1311,7 +1311,7 @@ namespace ranges { is_nothrow_move_constructible_v<_Wi>&& is_nothrow_move_constructible_v<_Bo>) // strengthened : _Value(_STD move(_Value_)), _Bound(_STD move(_Bound_)) { if constexpr (totally_ordered_with<_Wi, _Bo>) { - _STL_ASSERT(_Value_ <= _Bound_, "Per N4878 [range.iota.view]/8, the first argument must precede the " + _STL_ASSERT(_Value_ <= _Bound_, "Per N4928 [range.iota.view]/8, the first argument must precede the " "second when their types are totally ordered."); } } @@ -2018,7 +2018,7 @@ namespace ranges { _NODISCARD constexpr _Iterator begin() { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY( - _Pred, "N4861 [range.filter.view]/3 forbids calling begin on a filter_view that holds no predicate"); + _Pred, "N4928 [range.filter.view]/3 forbids calling begin on a filter_view that holds no predicate"); #endif // _CONTAINER_DEBUG_LEVEL > 0 if constexpr (forward_range<_Vw>) { if (this->_Has_cache()) { @@ -3015,7 +3015,7 @@ namespace ranges { is_nothrow_move_constructible_v<_Vw>) // strengthened : _Range(_STD move(_Range_)), _Count{_Count_} { #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Count_ >= 0, "Numer of elements to drop must be non-negative (N4861 [range.drop.view]/1"); + _STL_VERIFY(_Count_ >= 0, "Numer of elements to drop must be non-negative (N4928 [range.drop.view]/1"); #endif // _CONTAINER_DEBUG_LEVEL > 0 } @@ -3216,7 +3216,7 @@ namespace ranges { _NODISCARD constexpr auto begin() { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY( - _Pred, "N4885 [range.drop.while.view] forbids calling begin on a drop_while_view with no predicate"); + _Pred, "N4928 [range.drop.while.view]/3 forbids calling begin on a drop_while_view with no predicate"); #endif // _CONTAINER_DEBUG_LEVEL > 0 if constexpr (forward_range<_Vw>) { if (this->_Has_cache()) { @@ -6483,7 +6483,7 @@ namespace ranges { is_nothrow_move_constructible_v<_Vw>) /* strengthened */ : _Range(_STD move(_Range_)), _Count{_Count_} { #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Count > 0, "The window size must be positive (N4917 [range.slide.view]/1)"); + _STL_VERIFY(_Count > 0, "The window size must be positive (N4928 [range.slide.view]/1)"); #endif // _CONTAINER_DEBUG_LEVEL > 0 } diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index 654e687a75..0b04f8c33d 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -642,7 +642,7 @@ inline constexpr std::size_t contig_iterator_archetype_max = 34; #pragma warning(pop) struct iter_concept_example { - // bidirectional_iterator and Cpp17InputIterator, but not Cpp17ForwardIterator (N4820 [iterator.concepts.general]/2) + // bidirectional_iterator and Cpp17InputIterator, but not Cpp17ForwardIterator (N4928 [iterator.concepts.general]/2) using value_type = int; using difference_type = int; @@ -916,33 +916,33 @@ namespace iterator_traits_test { return true; } - // N4820 [iterator.traits]/3.2: "Otherwise, if I satisfies the exposition-only concept cpp17-input-iterator..." + // N4928 [iterator.traits]/3.2: "Otherwise, if I satisfies the exposition-only concept cpp17-input-iterator..." - // N4820 [iterator.traits]: + // N4928 [iterator.traits]: // * 3.2.1: "... Otherwise, pointer names void." // * 3.2.2: "... Otherwise, reference names iter_reference_t." // * 3.2.3.4 "... Otherwise, iterator_category names... input_iterator_tag." STATIC_ASSERT(check()); - // N4820 [iterator.traits]: + // N4928 [iterator.traits]: // * 3.2.1: "... Otherwise, pointer names void." // * 3.2.2: "... Otherwise, reference names iter_reference_t." // * 3.2.3.3 "... Otherwise, iterator_category names... forward_iterator_tag if I satisfies cpp17-forward-iterator." STATIC_ASSERT( check, no_such_type, forward_iterator_tag, double, long, void, double const&>()); STATIC_ASSERT(check()); - // N4820 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval().operator->()) is well-formed, then + // N4928 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval().operator->()) is well-formed, then // pointer names that type." STATIC_ASSERT(check>, no_such_type, forward_iterator_tag, double, long, double const*, double const&>()); - // N4820 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type, + // N4928 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type, // iterator_category names that type." STATIC_ASSERT(check>, no_such_type, output_iterator_tag, double, long, void, double const&>()); STATIC_ASSERT(check>, no_such_type, input_iterator_tag, double, long, void, double const&>()); - // N4820 [iterator.traits]: + // N4928 [iterator.traits]: // * 3.2.1: "... Otherwise, pointer names void." // * 3.2.2: "If the qualified-id I::reference is valid and denotes a type, reference names that type." // * 3.2.3.2 "... Otherwise, iterator_category names... bidirectional_iterator_tag if I satisfies @@ -950,11 +950,11 @@ namespace iterator_traits_test { STATIC_ASSERT( check, no_such_type, bidirectional_iterator_tag, double, long, void, double const&>()); STATIC_ASSERT(check()); - // N4820 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval().operator->()) is well-formed, then + // N4928 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval().operator->()) is well-formed, then // pointer names that type." STATIC_ASSERT(check>, no_such_type, bidirectional_iterator_tag, double, long, double const*, double const&>()); - // N4820 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type, + // N4928 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type, // iterator_category names that type." STATIC_ASSERT(check>, no_such_type, output_iterator_tag, double, long, void, double const&>()); @@ -963,7 +963,7 @@ namespace iterator_traits_test { STATIC_ASSERT(check>, no_such_type, forward_iterator_tag, double, long, void, double const&>()); - // N4820 [iterator.traits]: + // N4928 [iterator.traits]: // * 3.2.1: "... Otherwise, pointer names void." // * 3.2.2: "... Otherwise, reference names iter_reference_t." // * 3.2.3.1 "... Otherwise, iterator_category names... random_access_iterator_tag if I satisfies @@ -973,13 +973,13 @@ namespace iterator_traits_test { STATIC_ASSERT(check()); STATIC_ASSERT( check, no_such_type, random_access_iterator_tag, double, long, void, double const&>()); - // N4820 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval().operator->()) is well-formed, then + // N4928 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval().operator->()) is well-formed, then // pointer names that type." STATIC_ASSERT(check>, no_such_type, random_access_iterator_tag, double, long, double const*, double const&>()); STATIC_ASSERT(check>, no_such_type, random_access_iterator_tag, double, long, double const*, double const&>()); - // N4820 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type, + // N4928 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type, // iterator_category names that type." STATIC_ASSERT(check>, no_such_type, output_iterator_tag, double, long, void, double const&>()); @@ -998,7 +998,7 @@ namespace iterator_traits_test { STATIC_ASSERT(check>, no_such_type, contiguous_iterator_tag, double, long, void, double const&>()); - // N4820 [iterator.traits]/3.3: "Otherwise, if I satisfies the exposition-only concept cpp17-iterator..." + // N4928 [iterator.traits]/3.3: "Otherwise, if I satisfies the exposition-only concept cpp17-iterator..." template struct simple_output_iter : Base { simple_output_iter const& operator*() const; @@ -1012,13 +1012,13 @@ namespace iterator_traits_test { // "... otherwise, it names void." STATIC_ASSERT(check, no_such_type, output_iterator_tag, void, void, void, void>()); - // N4820 [iterator.traits]/3.4: "Otherwise, iterator_traits has no members by any of the above names." + // N4928 [iterator.traits]/3.4: "Otherwise, iterator_traits has no members by any of the above names." STATIC_ASSERT(has_empty_traits); STATIC_ASSERT(has_empty_traits); STATIC_ASSERT(has_empty_traits); STATIC_ASSERT(has_empty_traits); - // N4820 [iterator.traits]/5: "iterator_traits is specialized for pointers..." + // N4928 [iterator.traits]/5: "iterator_traits is specialized for pointers..." STATIC_ASSERT(check()); STATIC_ASSERT(check()); @@ -1039,7 +1039,7 @@ namespace iterator_cust_move_test { template concept can_iter_rvalue_ref = requires { typename iter_rvalue_reference_t; }; - // N4820 [iterator.cust.move]/1.1 "iter_move(E), if that expression is valid, with overload resolution..." + // N4928 [iterator.cust.move]/1.1 "iter_move(E), if that expression is valid, with overload resolution..." struct friend_hook { friend constexpr double iter_move(friend_hook) noexcept { return 3.14; @@ -1068,7 +1068,7 @@ namespace iterator_cust_move_test { STATIC_ASSERT(static_cast(ranges::iter_move(E1::x)) == 0); STATIC_ASSERT(noexcept(ranges::iter_move(E1::x))); - // N4820 [iterator.cust.move]/1.2.1 "if *E is an lvalue, std::move(*E)" + // N4928 [iterator.cust.move]/1.2.1 "if *E is an lvalue, std::move(*E)" static constexpr int some_ints[] = {0, 1, 2, 3}; STATIC_ASSERT(same_as, int&&>); STATIC_ASSERT(ranges::iter_move(&some_ints[1]) == 1); @@ -1115,7 +1115,7 @@ namespace iterator_cust_move_test { STATIC_ASSERT(same_as, int const&&>); // oblivious to nested types STATIC_ASSERT(ranges::iter_move(with_bogus_typedefs{}) == 1); - // N4820 [iterator.cust.move]/1.2.2 "otherwise, *E." + // N4928 [iterator.cust.move]/1.2.2 "otherwise, *E." struct ref_is_prvalue { int operator*() const { return 42; @@ -1129,7 +1129,7 @@ namespace iterator_cust_move_test { STATIC_ASSERT(same_as, int&&>); STATIC_ASSERT(!noexcept(ranges::iter_move(ref_is_xvalue{}))); - // N4820 [iterator.cust.move]/1.3 "Otherwise, ranges::iter_move(E) is ill-formed." + // N4928 [iterator.cust.move]/1.3 "Otherwise, ranges::iter_move(E) is ill-formed." STATIC_ASSERT(!can_iter_move); STATIC_ASSERT(!can_iter_move); STATIC_ASSERT(!can_iter_move); @@ -1146,7 +1146,7 @@ namespace iterator_cust_swap_test { template concept can_iter_swap = requires(T&& t, U&& u) { ranges::iter_swap(std::forward(t), std::forward(u)); }; - // N4820 [iterator.cust.swap]/4.1: "(void)iter_swap(E1, E2), if that expression is valid, with..." + // N4928 [iterator.cust.swap]/4.1: "(void)iter_swap(E1, E2), if that expression is valid, with..." namespace adl_barrier { template void iter_swap(T, U) = delete; @@ -1180,7 +1180,7 @@ namespace iterator_cust_swap_test { STATIC_ASSERT(bullet1); STATIC_ASSERT((ranges::iter_swap(E1::x, E1::x), true)); - // N4849 [iterator.cust.swap]/4.2: "Otherwise if the types of E1 and E2 each model indirectly_readable, and if the + // N4928 [iterator.cust.swap]/4.2: "Otherwise if the types of E1 and E2 each model indirectly_readable, and if the // reference types of E1 and E2 model swappable_with, then ranges::swap(*E1, *E2)." // clang-format off template @@ -1231,7 +1231,7 @@ namespace iterator_cust_swap_test { STATIC_ASSERT((ranges::iter_swap(swap_proxy_readable<0>{}, swap_proxy_readable<1>{}), true)); STATIC_ASSERT(noexcept(ranges::iter_swap(swap_proxy_readable<0>{}, swap_proxy_readable<1>{}))); - // N4820 [iterator.cust.swap]/4.3: "Otherwise, if the types T1 and T2 of E1 and E2 model + // N4928 [iterator.cust.swap]/4.3: "Otherwise, if the types T1 and T2 of E1 and E2 model // indirectly_movable_storable and indirectly_movable_storable..." // clang-format off template @@ -1257,7 +1257,7 @@ namespace iterator_cust_swap_test { STATIC_ASSERT(same_as{}, unswap_proxy_readable<1>{})), void>); STATIC_ASSERT(noexcept(ranges::iter_swap(unswap_proxy_readable<0>{}, unswap_proxy_readable<1>{}))); - // N4820 [iterator.cust.swap]/4.4: "Otherwise, ranges::iter_swap(E1, E2) is ill-formed." + // N4928 [iterator.cust.swap]/4.4: "Otherwise, ranges::iter_swap(E1, E2) is ill-formed." template concept bullet4 = (!can_iter_swap); From cd75cc452df2f8518882e3701da93d3ca8a1c578 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Wed, 25 Jan 2023 02:54:42 +0800 Subject: [PATCH 4/6] Skip some test cases for clang --- tests/std/tests/P2441R2_views_join_with/test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index 102574afc3..6da866b8aa 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -363,6 +363,9 @@ struct instantiator { Outer empty{span{}}; test_one(empty, "*#"sv, views::empty); } +#ifdef __clang__ // TRANSITION, Clang sometimes mishandles iterator/sentinel types. + if constexpr (ranges::forward_range || ranges::common_range) +#endif // __clang__ { // Range-of-rvalue delimiter Inner inner_ranges[] = {Inner{span{input[0]}}, Inner{span{input[1]}}, Inner{span{input[2]}}, Inner{span{input[3]}}, Inner{span{input[4]}}, Inner{span{input[5]}}, Inner{span{input[6]}}, From 3f037d39d45d438410a9c9ef2910a78f3a2a3025 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 26 Jan 2023 00:59:22 +0800 Subject: [PATCH 5/6] Buggy cases are analyzed, LLVM-60293 is reported --- tests/std/tests/P2441R2_views_join_with/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index 6da866b8aa..d5b965a47c 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -363,7 +363,7 @@ struct instantiator { Outer empty{span{}}; test_one(empty, "*#"sv, views::empty); } -#ifdef __clang__ // TRANSITION, Clang sometimes mishandles iterator/sentinel types. +#ifdef __clang__ // TRANSITION, LLVM-60293 if constexpr (ranges::forward_range || ranges::common_range) #endif // __clang__ { // Range-of-rvalue delimiter From 8f31bd255342e0edeb664efbffa480767d0d188c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 25 Jan 2023 18:05:21 -0800 Subject: [PATCH 6/6] Code review feedback. --- stl/inc/ranges | 2 +- .../P0896R4_ranges_iterator_machinery/test.cpp | 14 ++++++++------ tests/std/tests/P0896R4_views_transform/test.cpp | 3 +-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index c0ec9322c8..e10fb17680 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3015,7 +3015,7 @@ namespace ranges { is_nothrow_move_constructible_v<_Vw>) // strengthened : _Range(_STD move(_Range_)), _Count{_Count_} { #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Count_ >= 0, "Numer of elements to drop must be non-negative (N4928 [range.drop.view]/1"); + _STL_VERIFY(_Count_ >= 0, "Number of elements to drop must be non-negative (N4928 [range.drop.view]/1"); #endif // _CONTAINER_DEBUG_LEVEL > 0 } diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index 0b04f8c33d..09e34c9830 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -251,7 +251,7 @@ struct xvalue_random_iter { D operator-(xvalue_random_iter const&) const; xvalue_random_iter& operator+=(D); xvalue_random_iter operator+(D) const; - friend xvalue_random_iter operator+(D, const xvalue_random_iter&); + friend xvalue_random_iter operator+(D, xvalue_random_iter const&); }; template @@ -1012,7 +1012,7 @@ namespace iterator_traits_test { // "... otherwise, it names void." STATIC_ASSERT(check, no_such_type, output_iterator_tag, void, void, void, void>()); - // N4928 [iterator.traits]/3.4: "Otherwise, iterator_traits has no members by any of the above names." + // N4928 [iterator.traits]/3.4: "Otherwise, iterator_traits has no members by any of the above names." STATIC_ASSERT(has_empty_traits); STATIC_ASSERT(has_empty_traits); STATIC_ASSERT(has_empty_traits); @@ -1039,7 +1039,8 @@ namespace iterator_cust_move_test { template concept can_iter_rvalue_ref = requires { typename iter_rvalue_reference_t; }; - // N4928 [iterator.cust.move]/1.1 "iter_move(E), if that expression is valid, with overload resolution..." + // N4928 [iterator.cust.move]/1.1 "iter_move(E), if [...] iter_move(E) is a well-formed expression when [...] + // performing argument-dependent lookup only." struct friend_hook { friend constexpr double iter_move(friend_hook) noexcept { return 3.14; @@ -1146,7 +1147,8 @@ namespace iterator_cust_swap_test { template concept can_iter_swap = requires(T&& t, U&& u) { ranges::iter_swap(std::forward(t), std::forward(u)); }; - // N4928 [iterator.cust.swap]/4.1: "(void)iter_swap(E1, E2), if that expression is valid, with..." + // N4928 [iterator.cust.swap]/4.1: "(void)iter_swap(E1, E2), if [...] iter_swap(E1, E2) is a + // well-formed expression with overload resolution performed in a context [...]" namespace adl_barrier { template void iter_swap(T, U) = delete; @@ -1180,8 +1182,8 @@ namespace iterator_cust_swap_test { STATIC_ASSERT(bullet1); STATIC_ASSERT((ranges::iter_swap(E1::x, E1::x), true)); - // N4928 [iterator.cust.swap]/4.2: "Otherwise if the types of E1 and E2 each model indirectly_readable, and if the - // reference types of E1 and E2 model swappable_with, then ranges::swap(*E1, *E2)." + // N4928 [iterator.cust.swap]/4.2: "Otherwise, if the types of E1 and E2 each model indirectly_readable, + // and if the reference types of E1 and E2 model swappable_with, then ranges::swap(*E1, *E2)." // clang-format off template concept bullet2 = !bullet1 && indirectly_readable> diff --git a/tests/std/tests/P0896R4_views_transform/test.cpp b/tests/std/tests/P0896R4_views_transform/test.cpp index e0a219e333..887884c942 100644 --- a/tests/std/tests/P0896R4_views_transform/test.cpp +++ b/tests/std/tests/P0896R4_views_transform/test.cpp @@ -372,8 +372,7 @@ struct move_fn { template constexpr void test_xvalue_ranges(Rng&& rng) { - using ranges::transform_view, ranges::input_range, ranges::forward_range, ranges::bidirectional_range, - ranges::random_access_range, ranges::iterator_t, ranges::range_reference_t, ranges::range_value_t; + using ranges::transform_view, ranges::forward_range, ranges::iterator_t, ranges::range_reference_t; using V = views::all_t; using TV = transform_view;