diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h --- a/libcxx/include/__ranges/iota_view.h +++ b/libcxx/include/__ranges/iota_view.h @@ -9,6 +9,16 @@ #ifndef _LIBCPP___RANGES_IOTA_VIEW_H #define _LIBCPP___RANGES_IOTA_VIEW_H +#include <__compare/three_way_comparable.h> +#include <__concepts/arithmetic.h> +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__concepts/copyable.h> +#include <__concepts/equality_comparable.h> +#include <__concepts/invocable.h> +#include <__concepts/same_as.h> +#include <__concepts/semiregular.h> +#include <__concepts/totally_ordered.h> #include <__config> #include <__debug> #include <__functional/ranges_operations.h> @@ -21,7 +31,6 @@ #include <__ranges/view_interface.h> #include <__utility/forward.h> #include <__utility/move.h> -#include #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -212,11 +221,11 @@ return !(__x < __y); } -// friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) -// requires totally_ordered<_Start> && three_way_comparable<_Start> -// { -// return __x.__value_ <=> __y.__value_; -// } + friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires totally_ordered<_Start> && three_way_comparable<_Start> + { + return __x.__value_ <=> __y.__value_; + } _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(__iterator __i, difference_type __n) diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h --- a/libcxx/include/__ranges/transform_view.h +++ b/libcxx/include/__ranges/transform_view.h @@ -9,6 +9,13 @@ #ifndef _LIBCPP___RANGES_TRANSFORM_VIEW_H #define _LIBCPP___RANGES_TRANSFORM_VIEW_H +#include <__compare/three_way_comparable.h> +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__concepts/copyable.h> +#include <__concepts/derived_from.h> +#include <__concepts/equality_comparable.h> +#include <__concepts/invocable.h> #include <__config> #include <__functional/bind_back.h> #include <__functional/invoke.h> @@ -27,7 +34,6 @@ #include <__utility/forward.h> #include <__utility/in_place.h> #include <__utility/move.h> -#include #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -297,13 +303,14 @@ return __x.__current_ >= __y.__current_; } -// TODO: Fix this as soon as soon as three_way_comparable is implemented. -// _LIBCPP_HIDE_FROM_ABI -// friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) -// requires random_access_range<_Base> && three_way_comparable> -// { -// return __x.__current_ <=> __y.__current_; -// } +#if !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) + _LIBCPP_HIDE_FROM_ABI + friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> && three_way_comparable> + { + return __x.__current_ <=> __y.__current_; + } +#endif // !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(__iterator __i, difference_type __n) diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/compare.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/compare.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/compare.pass.cpp @@ -10,51 +10,59 @@ // UNSUPPORTED: libcpp-no-concepts // UNSUPPORTED: libcpp-has-no-incomplete-ranges -// transform_view::::operator{<,>,<=,>=} +// transform_view::::operator{<,>,<=,>=,==,!=,<=>} #include #include #include "test_macros.h" +#include "test_iterators.h" #include "../types.h" constexpr bool test() { { - std::ranges::transform_view transformView1; - auto iter1 = std::move(transformView1).begin(); - std::ranges::transform_view transformView2; - auto iter2 = std::move(transformView2).begin(); - assert(iter1 == iter2); - assert(iter1 + 1 != iter2); - assert(iter1 + 1 == iter2 + 1); + // Test a new-school iterator with operator<=>; the transform iterator should also have operator<=>. + using It = three_way_contiguous_iterator; + static_assert(std::three_way_comparable); + using R = std::ranges::transform_view, PlusOne>; + static_assert(std::three_way_comparable>); - assert(iter1 < iter1 + 1); - assert(iter1 + 1 > iter1); - assert(iter1 <= iter1 + 1); - assert(iter1 <= iter2); - assert(iter1 + 1 >= iter2); - assert(iter1 >= iter2); + int a[] = {1,2,3}; + R r = std::ranges::subrange(It(a), It(a+3)) | std::views::transform(PlusOne()); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + assert(!(iter1 < iter1)); assert(iter1 < iter2); assert(!(iter2 < iter1)); + assert(iter1 <= iter1); assert(iter1 <= iter2); assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); assert(!(iter1 > iter2)); assert(iter2 > iter1); + assert(iter1 >= iter1); assert(!(iter1 >= iter2)); assert(iter2 >= iter1); + assert(iter1 == iter1); assert(!(iter1 == iter2)); assert(iter2 == iter2); + assert(!(iter1 != iter1)); assert(iter1 != iter2); assert(!(iter2 != iter2)); + + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); } -// TODO: when three_way_comparable is implemented and std::is_eq is implemented, -// uncomment this. -// { -// std::ranges::transform_view transformView1; -// auto iter1 = transformView1.begin(); -// std::ranges::transform_view transformView2; -// auto iter2 = transformView2.begin(); -// -// assert(std::is_eq(iter1 <=> iter2)); -// assert(std::is_lteq(iter1 <=> iter2)); -// ++iter2; -// assert(std::is_neq(iter1 <=> iter2)); -// assert(std::is_lt(iter1 <=> iter2)); -// assert(std::is_gt(iter2 <=> iter1)); -// assert(std::is_gteq(iter2 <=> iter1)); -// -// static_assert( std::three_way_comparable>>); -// static_assert(!std::three_way_comparable>>); -// } + { + // Test an old-school iterator with no operator<=>; the transform iterator shouldn't have operator<=> either. + using It = random_access_iterator; + static_assert(!std::three_way_comparable); + using R = std::ranges::transform_view, PlusOne>; + static_assert(!std::three_way_comparable>); + + int a[] = {1,2,3}; + R r = std::ranges::subrange(It(a), It(a+3)) | std::views::transform(PlusOne()); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + assert(!(iter1 < iter1)); assert(iter1 < iter2); assert(!(iter2 < iter1)); + assert(iter1 <= iter1); assert(iter1 <= iter2); assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); assert(!(iter1 > iter2)); assert(iter2 > iter1); + assert(iter1 >= iter1); assert(!(iter1 >= iter2)); assert(iter2 >= iter1); + assert(iter1 == iter1); assert(!(iter1 == iter2)); assert(iter2 == iter2); + assert(!(iter1 != iter1)); assert(iter1 != iter2); assert(!(iter2 != iter2)); + } return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h --- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h +++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h @@ -129,14 +129,6 @@ constexpr CountedIter end() const { return CountedIter(ForwardIter(globalBuff + 8)); } }; -using ThreeWayCompIter = three_way_contiguous_iterator; -struct ThreeWayCompView : std::ranges::view_base { - constexpr ThreeWayCompIter begin() { return ThreeWayCompIter(globalBuff); } - constexpr ThreeWayCompIter begin() const { return ThreeWayCompIter(globalBuff); } - constexpr ThreeWayCompIter end() { return ThreeWayCompIter(globalBuff + 8); } - constexpr ThreeWayCompIter end() const { return ThreeWayCompIter(globalBuff + 8); } -}; - struct TimesTwo { constexpr int operator()(int x) const { return x * 2; } }; diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/compare.pass.cpp --- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/compare.pass.cpp +++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/compare.pass.cpp @@ -10,69 +10,79 @@ // UNSUPPORTED: libcpp-no-concepts // UNSUPPORTED: libcpp-has-no-incomplete-ranges -// friend constexpr bool operator<(const iterator& x, const iterator& y) -// requires totally_ordered; -// friend constexpr bool operator>(const iterator& x, const iterator& y) -// requires totally_ordered; -// friend constexpr bool operator<=(const iterator& x, const iterator& y) -// requires totally_ordered; -// friend constexpr bool operator>=(const iterator& x, const iterator& y) -// requires totally_ordered; -// friend constexpr bool operator==(const iterator& x, const iterator& y) -// requires equality_comparable; - -// TODO: test spaceship operator once it's implemented. +// iota_view::::operator{<,>,<=,>=,==,!=,<=>} #include -#include +#include #include "test_macros.h" +#include "test_iterators.h" #include "../types.h" constexpr bool test() { { - const std::ranges::iota_view io(0); - assert( io.begin() == io.begin() ); - assert( io.begin() != std::ranges::next(io.begin())); - assert( io.begin() < std::ranges::next(io.begin())); - assert(std::ranges::next(io.begin()) > io.begin() ); - assert( io.begin() <= std::ranges::next(io.begin())); - assert(std::ranges::next(io.begin()) >= io.begin() ); - assert( io.begin() <= io.begin() ); - assert( io.begin() >= io.begin() ); - } - { - std::ranges::iota_view io(0); - assert( io.begin() == io.begin() ); - assert( io.begin() != std::ranges::next(io.begin())); - assert( io.begin() < std::ranges::next(io.begin())); - assert(std::ranges::next(io.begin()) > io.begin() ); - assert( io.begin() <= std::ranges::next(io.begin())); - assert(std::ranges::next(io.begin()) >= io.begin() ); - assert( io.begin() <= io.begin() ); - assert( io.begin() >= io.begin() ); + // Test `int`, which has operator<=>; the iota iterator should also have operator<=>. + using R = std::ranges::iota_view; + static_assert(std::three_way_comparable>); + + std::same_as auto r = std::views::iota(42); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + assert(!(iter1 < iter1)); assert(iter1 < iter2); assert(!(iter2 < iter1)); + assert(iter1 <= iter1); assert(iter1 <= iter2); assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); assert(!(iter1 > iter2)); assert(iter2 > iter1); + assert(iter1 >= iter1); assert(!(iter1 >= iter2)); assert(iter2 >= iter1); + assert(iter1 == iter1); assert(!(iter1 == iter2)); assert(iter2 == iter2); + assert(!(iter1 != iter1)); assert(iter1 != iter2); assert(!(iter2 != iter2)); + + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); } + { - const std::ranges::iota_view io(SomeInt(0)); - assert( io.begin() == io.begin() ); - assert( io.begin() != std::ranges::next(io.begin())); - assert( io.begin() < std::ranges::next(io.begin())); - assert(std::ranges::next(io.begin()) > io.begin() ); - assert( io.begin() <= std::ranges::next(io.begin())); - assert(std::ranges::next(io.begin()) >= io.begin() ); - assert( io.begin() <= io.begin() ); - assert( io.begin() >= io.begin() ); + // Test a new-school iterator with operator<=>; the iota iterator should also have operator<=>. + using It = three_way_contiguous_iterator; + static_assert(std::three_way_comparable); + using R = std::ranges::iota_view; + static_assert(std::three_way_comparable>); + + int a[] = {1,2,3}; + std::same_as auto r = std::views::iota(It(a)); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + assert(!(iter1 < iter1)); assert(iter1 < iter2); assert(!(iter2 < iter1)); + assert(iter1 <= iter1); assert(iter1 <= iter2); assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); assert(!(iter1 > iter2)); assert(iter2 > iter1); + assert(iter1 >= iter1); assert(!(iter1 >= iter2)); assert(iter2 >= iter1); + assert(iter1 == iter1); assert(!(iter1 == iter2)); assert(iter2 == iter2); + assert(!(iter1 != iter1)); assert(iter1 != iter2); assert(!(iter2 != iter2)); + + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); } + { - std::ranges::iota_view io(SomeInt(0)); - assert( io.begin() == io.begin() ); - assert( io.begin() != std::ranges::next(io.begin())); - assert( io.begin() < std::ranges::next(io.begin())); - assert(std::ranges::next(io.begin()) > io.begin() ); - assert( io.begin() <= std::ranges::next(io.begin())); - assert(std::ranges::next(io.begin()) >= io.begin() ); - assert( io.begin() <= io.begin() ); - assert( io.begin() >= io.begin() ); + // Test an old-school iterator with no operator<=>; the iota iterator shouldn't have operator<=> either. + using It = random_access_iterator; + static_assert(!std::three_way_comparable); + using R = std::ranges::iota_view; + static_assert(!std::three_way_comparable>); + + int a[] = {1,2,3}; + std::same_as auto r = std::views::iota(It(a)); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + assert(!(iter1 < iter1)); assert(iter1 < iter2); assert(!(iter2 < iter1)); + assert(iter1 <= iter1); assert(iter1 <= iter2); assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); assert(!(iter1 > iter2)); assert(iter2 > iter1); + assert(iter1 >= iter1); assert(!(iter1 >= iter2)); assert(iter2 >= iter1); + assert(iter1 == iter1); assert(!(iter1 == iter2)); assert(iter2 == iter2); + assert(!(iter1 != iter1)); assert(iter1 != iter2); assert(!(iter2 != iter2)); } return true;