diff --git a/libcxx/include/__iterator/iter_move.h b/libcxx/include/__iterator/iter_move.h --- a/libcxx/include/__iterator/iter_move.h +++ b/libcxx/include/__iterator/iter_move.h @@ -25,58 +25,59 @@ #if !defined(_LIBCPP_HAS_NO_RANGES) -namespace ranges::__iter_move { +// [iterator.cust.move] + +namespace ranges { +namespace __iter_move { + void iter_move(); -template -concept __unqualified_iter_move = requires(_Ip&& __i) { - iter_move(_VSTD::forward<_Ip>(__i)); -}; +template +concept __unqualified_iter_move = + __class_or_enum> && + requires (_Tp&& __t) { + iter_move(_VSTD::forward<_Tp>(__t)); + }; + +template +concept __operator_star = + !__unqualified_iter_move<_Tp> && + requires (_Tp&& __t) { + *_VSTD::forward<_Tp>(__t); + }; -// [iterator.cust.move]/1 -// The name ranges::iter_move denotes a customization point object. -// The expression ranges::iter_move(E) for a subexpression E is -// expression-equivalent to: struct __fn { - // [iterator.cust.move]/1.1 - // iter_move(E), if E has class or enumeration type and iter_move(E) is a - // well-formed expression when treated as an unevaluated operand, [...] - template - requires __class_or_enum> && __unqualified_iter_move<_Ip> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Ip&& __i) const - noexcept(noexcept(iter_move(_VSTD::forward<_Ip>(__i)))) + template + requires __unqualified_iter_move<_Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr + decltype(auto) operator()(_Tp&& __t) const + noexcept(noexcept(iter_move(_VSTD::forward<_Tp>(__t)))) { - return iter_move(_VSTD::forward<_Ip>(__i)); + return iter_move(_VSTD::forward<_Tp>(__t)); } - // [iterator.cust.move]/1.2 - // Otherwise, if the expression *E is well-formed: - // 1.2.1 if *E is an lvalue, std::move(*E); - // 1.2.2 otherwise, *E. - template - requires (!(__class_or_enum> && __unqualified_iter_move<_Ip>)) && - requires(_Ip&& __i) { *_VSTD::forward<_Ip>(__i); } - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Ip&& __i) const - noexcept(noexcept(*_VSTD::forward<_Ip>(__i))) + template + requires __operator_star<_Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr + decltype(auto) operator()(_Tp&& __t) const + noexcept(noexcept(*_VSTD::forward<_Tp>(__t))) { - if constexpr (is_lvalue_reference_v(__i))>) { - return _VSTD::move(*_VSTD::forward<_Ip>(__i)); + if constexpr (is_lvalue_reference_v(__t))>) { + return _VSTD::move(*_VSTD::forward<_Tp>(__t)); } else { - return *_VSTD::forward<_Ip>(__i); + return *_VSTD::forward<_Tp>(__t); } } - - // [iterator.cust.move]/1.3 - // Otherwise, ranges::iter_move(E) is ill-formed. }; -} // namespace ranges::__iter_move +} // namespace __iter_move -namespace ranges::inline __cpo { +inline namespace __cpo { inline constexpr auto iter_move = __iter_move::__fn{}; -} +} // namespace __cpo +} // namespace ranges template<__dereferenceable _Tp> -requires requires(_Tp& __t) { { ranges::iter_move(__t) } -> __referenceable; } + requires requires(_Tp& __t) { { ranges::iter_move(__t) } -> __referenceable; } using iter_rvalue_reference_t = decltype(ranges::iter_move(declval<_Tp&>())); #endif // !_LIBCPP_HAS_NO_RANGES diff --git a/libcxx/include/__iterator/iter_swap.h b/libcxx/include/__iterator/iter_swap.h --- a/libcxx/include/__iterator/iter_swap.h +++ b/libcxx/include/__iterator/iter_swap.h @@ -30,46 +30,54 @@ namespace ranges { namespace __iter_swap { + template void iter_swap(_I1, _I2) = delete; template - concept __unqualified_iter_swap = requires(_T1&& __x, _T2&& __y) { - iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y)); - }; + concept __unqualified_iter_swap = + (__class_or_enum> || __class_or_enum>) && + requires (_T1&& __x, _T2&& __y) { + iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y)); + }; template - concept __readable_swappable = - indirectly_readable<_T1> && indirectly_readable<_T2> && + concept __ranges_swappable = + !__unqualified_iter_swap<_T1, _T2> && + indirectly_readable<_T1> && + indirectly_readable<_T2> && swappable_with, iter_reference_t<_T2>>; + template + concept __iter_exchange_movable = + !__unqualified_iter_swap<_T1, _T2> && + !__ranges_swappable<_T1, _T2> && + indirectly_movable_storable, remove_reference_t<_T2>> && + indirectly_movable_storable, remove_reference_t<_T1>>; + struct __fn { template requires __unqualified_iter_swap<_T1, _T2> - _LIBCPP_HIDE_FROM_ABI - constexpr void operator()(_T1&& __x, _T2&& __y) const + _LIBCPP_HIDE_FROM_ABI constexpr + void operator()(_T1&& __x, _T2&& __y) const noexcept(noexcept(iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y)))) { - (void)iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y)); + iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y)); } template - requires (!__unqualified_iter_swap<_T1, _T2>) && - __readable_swappable<_T1, _T2> - _LIBCPP_HIDE_FROM_ABI - constexpr void operator()(_T1&& __x, _T2&& __y) const + requires __ranges_swappable<_T1, _T2> + _LIBCPP_HIDE_FROM_ABI constexpr + void operator()(_T1&& __x, _T2&& __y) const noexcept(noexcept(ranges::swap(*_VSTD::forward<_T1>(__x), *_VSTD::forward<_T2>(__y)))) { ranges::swap(*_VSTD::forward<_T1>(__x), *_VSTD::forward<_T2>(__y)); } template - requires (!__unqualified_iter_swap<_T1, _T2> && - !__readable_swappable<_T1, _T2>) && - indirectly_movable_storable<_T1, _T2> && - indirectly_movable_storable<_T2, _T1> - _LIBCPP_HIDE_FROM_ABI - constexpr void operator()(_T1&& __x, _T2&& __y) const + requires __iter_exchange_movable<_T1, _T2> + _LIBCPP_HIDE_FROM_ABI constexpr + void operator()(_T1&& __x, _T2&& __y) const noexcept(noexcept(iter_value_t<_T2>(ranges::iter_move(__y))) && noexcept(*__y = ranges::iter_move(__x)) && noexcept(*_VSTD::forward<_T1>(__x) = declval>())) @@ -79,12 +87,11 @@ *_VSTD::forward<_T1>(__x) = _VSTD::move(__old); } }; -} // end namespace __iter_swap +} // namespace __iter_swap inline namespace __cpo { inline constexpr auto iter_swap = __iter_swap::__fn{}; } // namespace __cpo - } // namespace ranges template diff --git a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp --- a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp +++ b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp @@ -21,6 +21,8 @@ #include "../unqualified_lookup_wrapper.h" +using IterMoveT = decltype(std::ranges::iter_move); + // Wrapper around an iterator for testing `iter_move` when an unqualified call to `iter_move` isn't // possible. template @@ -113,7 +115,7 @@ constexpr bool operator==(WithoutADL const&) const; }; -constexpr bool check_iter_move() { +constexpr bool test() { constexpr int full_size = 100; constexpr int half_size = full_size / 2; constexpr int reset = 0; @@ -173,18 +175,19 @@ return true; } -template -concept can_iter_move = requires (T t) { std::ranges::iter_move(t); }; +static_assert(!std::is_invocable_v); // too many arguments +static_assert(!std::is_invocable_v); -int main(int, char**) { - static_assert(check_iter_move()); - check_iter_move(); +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(std::is_invocable_v**>); +static_assert(std::is_invocable_v**&>); - // Make sure that `iter_move` SFINAEs away when the type can't be iter_move'd - { - struct NoIterMove { }; - static_assert(!can_iter_move); - } +int main(int, char**) +{ + test(); + static_assert(test()); return 0; } diff --git a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap/iter_swap.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap/iter_swap.pass.cpp --- a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap/iter_swap.pass.cpp +++ b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap/iter_swap.pass.cpp @@ -24,7 +24,7 @@ struct HasIterSwap { int &value_; - explicit HasIterSwap(int &value) : value_(value) { assert(value == 0); } + constexpr explicit HasIterSwap(int &value) : value_(value) { assert(value == 0); } friend constexpr void iter_swap(HasIterSwap& a, HasIterSwap& b) { a.value_ = 1; @@ -56,7 +56,7 @@ struct HasRangesSwap { int &value_; - explicit HasRangesSwap(int &value) : value_(value) { assert(value == 0); } + constexpr explicit HasRangesSwap(int &value) : value_(value) { assert(value == 0); } friend constexpr void swap(HasRangesSwap& a, HasRangesSwap& b) { a.value_ = 1; @@ -72,9 +72,9 @@ using value_type = HasRangesSwap; HasRangesSwap &value_; - explicit HasRangesSwapWrapper(HasRangesSwap &value) : value_(value) {} + constexpr explicit HasRangesSwapWrapper(HasRangesSwap &value) : value_(value) {} - HasRangesSwap& operator*() const { return value_; } + constexpr HasRangesSwap& operator*() const { return value_; } }; static_assert( std::is_invocable_v); @@ -86,7 +86,7 @@ struct A { bool value = false; - A& operator=(const B&) { + constexpr A& operator=(const B&) { value = true; return *this; }; @@ -94,7 +94,7 @@ struct B { bool value = false; - B& operator=(const A&) { + constexpr B& operator=(const A&) { value = true; return *this; }; @@ -111,7 +111,7 @@ MoveOnly1(const MoveOnly1&) = delete; MoveOnly1& operator=(const MoveOnly1&) = delete; - MoveOnly1& operator=(MoveOnly2 &&) { + constexpr MoveOnly1& operator=(MoveOnly2 &&) { value = true; return *this; }; @@ -126,13 +126,14 @@ MoveOnly2(const MoveOnly2&) = delete; MoveOnly2& operator=(const MoveOnly2&) = delete; - MoveOnly2& operator=(MoveOnly1 &&) { + constexpr MoveOnly2& operator=(MoveOnly1 &&) { value = true; return *this; }; }; -int main(int, char**) { +constexpr bool test() +{ { int value1 = 0; int value2 = 0; @@ -140,7 +141,6 @@ std::ranges::iter_swap(a, b); assert(value1 == 1 && value2 == 1); } - { int value1 = 0; int value2 = 0; @@ -149,7 +149,6 @@ std::ranges::iter_swap(cWrapper, dWrapper); assert(value1 == 1 && value2 == 1); } - { int value1 = 0; int value2 = 0; @@ -157,7 +156,6 @@ std::ranges::iter_swap(HasRangesSwapWrapper(c), HasRangesSwapWrapper(d)); assert(value1 == 1 && value2 == 1); } - { A e; B f; A *ePtr = &e; @@ -165,19 +163,20 @@ std::ranges::iter_swap(ePtr, fPtr); assert(e.value && f.value); } - { MoveOnly1 g; MoveOnly2 h; std::ranges::iter_swap(&g, &h); assert(g.value && h.value); } - { auto arr = std::array(); std::ranges::iter_swap(arr.begin(), arr.begin() + 1); - assert(arr[0].moves() == 1 && arr[1].moves() == 2); + if (std::is_constant_evaluated()) { + assert(arr[0].moves() == 1 && arr[1].moves() == 3); + } else { + assert(arr[0].moves() == 1 && arr[1].moves() == 2); + } } - { int buff[2] = {1, 2}; std::ranges::iter_swap(buff + 0, buff + 1); @@ -201,6 +200,27 @@ std::ranges::iter_swap(contiguous_iterator(buff), contiguous_iterator(buff + 1)); assert(buff[0] == 2 && buff[1] == 1); } + return true; +} + +static_assert(!std::is_invocable_v); // too few arguments +static_assert(!std::is_invocable_v); // too many arguments +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(std::is_invocable_v**, Holder**>); +static_assert(std::is_invocable_v**, Holder**&>); +static_assert(std::is_invocable_v**&, Holder**>); +static_assert(std::is_invocable_v**&, Holder**&>); + +int main(int, char**) +{ + test(); + static_assert(test()); return 0; } diff --git a/libcxx/test/std/ranges/range.req/range.refinements/bidirectional_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.refinements/bidirectional_range.compile.pass.cpp --- a/libcxx/test/std/ranges/range.req/range.refinements/bidirectional_range.compile.pass.cpp +++ b/libcxx/test/std/ranges/range.req/range.refinements/bidirectional_range.compile.pass.cpp @@ -17,8 +17,6 @@ #include "test_range.h" - - template