diff --git a/libstdc++-v3/include/bits/ranges_cmp.h b/libstdc++-v3/include/bits/ranges_cmp.h index c6451427beb..e2bf97ed4cd 100644 --- a/libstdc++-v3/include/bits/ranges_cmp.h +++ b/libstdc++-v3/include/bits/ranges_cmp.h @@ -71,10 +71,11 @@ namespace ranges = requires (_Tp&& __t, _Up&& __u) { { __t < __u } -> same_as; } && convertible_to<_Tp, const volatile void*> && convertible_to<_Up, const volatile void*> - && (! requires(_Tp&& __t, _Up&& __u) + && ! requires(_Tp&& __t, _Up&& __u) { operator<(std::forward<_Tp>(__t), std::forward<_Up>(__u)); } - && ! requires(_Tp&& __t, _Up&& __u) - { std::forward<_Tp>(__t).operator<(std::forward<_Up>(__u)); }); + && ! requires(_Tp&& __t, _Up&& __u) + { std::forward<_Tp>(__t).operator<(std::forward<_Up>(__u)); } + && std::__detail::__not_overloaded_spaceship<_Tp, _Up>; } // namespace __detail // [range.cmp] Concept-constrained comparisons diff --git a/libstdc++-v3/include/bits/stl_function.h b/libstdc++-v3/include/bits/stl_function.h index 659025c4bf9..0600de72b10 100644 --- a/libstdc++-v3/include/bits/stl_function.h +++ b/libstdc++-v3/include/bits/stl_function.h @@ -59,6 +59,9 @@ #if __cplusplus > 201103L #include #endif +#if __cplusplus >= 202002L +#include +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -525,8 +528,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(noexcept(std::forward<_Tp>(__t) > std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) > std::forward<_Up>(__u)) { - return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), - __ptr_cmp<_Tp, _Up>{}); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr + if constexpr (__ptr_cmp<_Tp, _Up>) + return greater{}( + static_cast(std::forward<_Tp>(__t)), + static_cast(std::forward<_Up>(__u))); + else + return std::forward<_Tp>(__t) > std::forward<_Up>(__u); +#pragma GCC diagnostic pop } template @@ -537,20 +547,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef __is_transparent is_transparent; private: - template - static constexpr decltype(auto) - _S_cmp(_Tp&& __t, _Up&& __u, false_type) - { return std::forward<_Tp>(__t) > std::forward<_Up>(__u); } - - template - static constexpr bool - _S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept +#if __cplusplus >= 202002L + template + static constexpr bool __ptr_cmp = requires { - return greater{}( - static_cast(std::forward<_Tp>(__t)), - static_cast(std::forward<_Up>(__u))); - } - + requires + ! requires + { operator>(std::declval<_Tp>(), std::declval<_Up>()); } + && ! requires + { std::declval<_Tp>().operator>(std::declval<_Up>()); } + && __detail::__not_overloaded_spaceship<_Tp, _Up> + && is_convertible_v<_Tp, const volatile void*> + && is_convertible_v<_Up, const volatile void*>; + }; +#else // True if there is no viable operator> member function. template struct __not_overloaded2 : true_type { }; @@ -572,9 +582,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : false_type { }; template - using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>, - is_convertible<_Tp, const volatile void*>, - is_convertible<_Up, const volatile void*>>; + static constexpr bool __ptr_cmp = __and_< + __not_overloaded<_Tp, _Up>, + is_convertible<_Tp, const volatile void*>, + is_convertible<_Up, const volatile void*>>::value; +#endif }; /// One of the @link comparison_functors comparison functors@endlink. @@ -587,8 +599,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(noexcept(std::forward<_Tp>(__t) < std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) < std::forward<_Up>(__u)) { - return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), - __ptr_cmp<_Tp, _Up>{}); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr + if constexpr (__ptr_cmp<_Tp, _Up>) + return less{}( + static_cast(std::forward<_Tp>(__t)), + static_cast(std::forward<_Up>(__u))); + else + return std::forward<_Tp>(__t) < std::forward<_Up>(__u); +#pragma GCC diagnostic pop } template @@ -599,20 +618,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef __is_transparent is_transparent; private: - template - static constexpr decltype(auto) - _S_cmp(_Tp&& __t, _Up&& __u, false_type) - { return std::forward<_Tp>(__t) < std::forward<_Up>(__u); } - - template - static constexpr bool - _S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept +#if __cplusplus >= 202002L + template + static constexpr bool __ptr_cmp = requires { - return less{}( - static_cast(std::forward<_Tp>(__t)), - static_cast(std::forward<_Up>(__u))); - } - + requires + ! requires + { operator<(std::declval<_Tp>(), std::declval<_Up>()); } + && ! requires + { std::declval<_Tp>().operator<(std::declval<_Up>()); } + && __detail::__not_overloaded_spaceship<_Tp, _Up> + && is_convertible_v<_Tp, const volatile void*> + && is_convertible_v<_Up, const volatile void*>; + }; +#else // True if there is no viable operator< member function. template struct __not_overloaded2 : true_type { }; @@ -634,9 +653,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : false_type { }; template - using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>, - is_convertible<_Tp, const volatile void*>, - is_convertible<_Up, const volatile void*>>; + static constexpr bool __ptr_cmp = __and_< + __not_overloaded<_Tp, _Up>, + is_convertible<_Tp, const volatile void*>, + is_convertible<_Up, const volatile void*>>::value; +#endif }; /// One of the @link comparison_functors comparison functors@endlink. @@ -649,8 +670,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(noexcept(std::forward<_Tp>(__t) >= std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) >= std::forward<_Up>(__u)) { - return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), - __ptr_cmp<_Tp, _Up>{}); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr + if constexpr (__ptr_cmp<_Tp, _Up>) + return greater_equal{}( + static_cast(std::forward<_Tp>(__t)), + static_cast(std::forward<_Up>(__u))); + else + return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); +#pragma GCC diagnostic pop } template @@ -661,20 +689,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef __is_transparent is_transparent; private: - template - static constexpr decltype(auto) - _S_cmp(_Tp&& __t, _Up&& __u, false_type) - { return std::forward<_Tp>(__t) >= std::forward<_Up>(__u); } - - template - static constexpr bool - _S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept +#if __cplusplus >= 202002L + template + static constexpr bool __ptr_cmp = requires { - return greater_equal{}( - static_cast(std::forward<_Tp>(__t)), - static_cast(std::forward<_Up>(__u))); - } - + requires + ! requires + { operator>=(std::declval<_Tp>(), std::declval<_Up>()); } + && ! requires + { std::declval<_Tp>().operator>=(std::declval<_Up>()); } + && __detail::__not_overloaded_spaceship<_Tp, _Up> + && is_convertible_v<_Tp, const volatile void*> + && is_convertible_v<_Up, const volatile void*>; + }; +#else // True if there is no viable operator>= member function. template struct __not_overloaded2 : true_type { }; @@ -696,9 +724,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : false_type { }; template - using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>, - is_convertible<_Tp, const volatile void*>, - is_convertible<_Up, const volatile void*>>; + static constexpr bool __ptr_cmp = __and_< + __not_overloaded<_Tp, _Up>, + is_convertible<_Tp, const volatile void*>, + is_convertible<_Up, const volatile void*>>::value; +#endif }; /// One of the @link comparison_functors comparison functors@endlink. @@ -711,8 +741,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(noexcept(std::forward<_Tp>(__t) <= std::forward<_Up>(__u))) -> decltype(std::forward<_Tp>(__t) <= std::forward<_Up>(__u)) { - return _S_cmp(std::forward<_Tp>(__t), std::forward<_Up>(__u), - __ptr_cmp<_Tp, _Up>{}); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr + if constexpr (__ptr_cmp<_Tp, _Up>) + return less_equal{}( + static_cast(std::forward<_Tp>(__t)), + static_cast(std::forward<_Up>(__u))); + else + return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); +#pragma GCC diagnostic pop } template @@ -723,20 +760,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef __is_transparent is_transparent; private: - template - static constexpr decltype(auto) - _S_cmp(_Tp&& __t, _Up&& __u, false_type) - { return std::forward<_Tp>(__t) <= std::forward<_Up>(__u); } - - template - static constexpr bool - _S_cmp(_Tp&& __t, _Up&& __u, true_type) noexcept +#if __cplusplus >= 202002L + template + static constexpr bool __ptr_cmp = requires { - return less_equal{}( - static_cast(std::forward<_Tp>(__t)), - static_cast(std::forward<_Up>(__u))); - } - + requires + ! requires + { operator<=(std::declval<_Tp>(), std::declval<_Up>()); } + && ! requires + { std::declval<_Tp>().operator<=(std::declval<_Up>()); } + && __detail::__not_overloaded_spaceship<_Tp, _Up> + && is_convertible_v<_Tp, const volatile void*> + && is_convertible_v<_Up, const volatile void*>; + }; +#else // True if there is no viable operator<= member function. template struct __not_overloaded2 : true_type { }; @@ -758,9 +795,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : false_type { }; template - using __ptr_cmp = __and_<__not_overloaded<_Tp, _Up>, - is_convertible<_Tp, const volatile void*>, - is_convertible<_Up, const volatile void*>>; + static constexpr bool __ptr_cmp = __and_< + __not_overloaded<_Tp, _Up>, + is_convertible<_Tp, const volatile void*>, + is_convertible<_Up, const volatile void*>>::value; +#endif }; #else // < C++14 diff --git a/libstdc++-v3/include/std/concepts b/libstdc++-v3/include/std/concepts index 9c687b03e80..7673443f33a 100644 --- a/libstdc++-v3/include/std/concepts +++ b/libstdc++-v3/include/std/concepts @@ -405,6 +405,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template concept strict_weak_order = relation<_Rel, _Tp, _Up>; + namespace __detail + { + // operator<=> are automatically reversed, so we need to consider + // both directions if types are different. + template + concept __not_overloaded_spaceship + = ! requires(_Tp&& __t, _Up&& __u) + { operator<=>(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u)); } + && ! requires(_Tp&& __t, _Up&& __u) + { static_cast<_Tp&&>(__t).operator<=>(static_cast<_Up&&>(__u)); } + && (is_same_v<_Tp, _Up> + || (! requires(_Tp&& __t, _Up&& __u) + { operator<=>(static_cast<_Up&&>(__u), static_cast<_Tp&&>(__t)); } + && ! requires(_Tp&& __t, _Up&& __u) + { static_cast<_Up&&>(__u).operator<=>(static_cast<_Tp&&>(__t)); })); + } _GLIBCXX_END_NAMESPACE_VERSION } // namespace #endif // __cpp_lib_concepts diff --git a/libstdc++-v3/libsupc++/compare b/libstdc++-v3/libsupc++/compare index a00bbefb069..3847d0fb141 100644 --- a/libstdc++-v3/libsupc++/compare +++ b/libstdc++-v3/libsupc++/compare @@ -560,10 +560,7 @@ namespace std _GLIBCXX_VISIBILITY(default) { static_cast<_Tp&&>(__t) <=> static_cast<_Up&&>(__u); } && convertible_to<_Tp, const volatile void*> && convertible_to<_Up, const volatile void*> - && ! requires(_Tp&& __t, _Up&& __u) - { operator<=>(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u)); } - && ! requires(_Tp&& __t, _Up&& __u) - { static_cast<_Tp&&>(__t).operator<=>(static_cast<_Up&&>(__u)); }; + && __not_overloaded_spaceship<_Tp, _Up>; } // namespace __detail // _GLIBCXX_RESOLVE_LIB_DEFECTS diff --git a/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer_spaceship.cc b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer_spaceship.cc new file mode 100644 index 00000000000..80fa94b5dbf --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/comparisons_pointer_spaceship.cc @@ -0,0 +1,336 @@ +// { dg-do run { target c++20 } } + +#include +#include +#include +#include + +constexpr const char arr[] = "efgh\0abcd\0ijkl"; +constexpr const char* s1 = arr; +constexpr const char* s2 = arr+5; +constexpr const char* s3 = arr+6; + +struct CStrNone +{ + const char* str; + + constexpr + operator const char*() const + { return str; } +}; + +template +struct CStrMem +{ + const char* str; + + constexpr + operator const char*() const + { return str; } + + auto operator<=>(CStrMem const& rhs) const + { return ResultCreator::create(std::strcmp(this->str, rhs.str)); } + + auto operator<=>(const char* rhs) const + { return ResultCreator::create(std::strcmp(this->str, rhs)); } +}; + +template +struct CStrFriend +{ + const char* str; + + constexpr + operator const char*() const + { return str; } + + friend auto operator<=>(CStrFriend lhs, CStrFriend rhs) + { return ResultCreator::create(std::strcmp(lhs.str, rhs.str)); } + + friend auto operator<=>(CStrFriend lhs, const char* rhs) + { return ResultCreator::create(std::strcmp(lhs.str, rhs)); } +}; + +template +struct CStrFree +{ + const char* str; + + constexpr + operator const char*() const + { return str; } +}; + +template +auto operator<=>(CStrFree lhs, CStrFree rhs) +{ return RC::create(std::strcmp(lhs.str, rhs.str)); } + +template +auto operator<=>(CStrFree lhs, const char* rhs) +{ return RC::create(std::strcmp(lhs.str, rhs)); } + +template +struct CStrMixed +{ + const char* str; + + constexpr + operator const char*() const + { return str; } +}; + +template +auto operator<=>(CStrMixed lhs, CStrMixed rhs) +{ return RC::create(std::strcmp(lhs.str, rhs.str)); } + +template +auto operator<=>(CStrMixed lhs, CStrFree rhs) +{ return RC::create(std::strcmp(lhs.str, rhs.str)); } + +// If the type returned from shapeship does not support relational +// operators, then synthesized operators are ill-formed, SFINAEable. +struct ReturnVoid +{ + constexpr static void + create(int) { } +}; + +struct NoOperators +{ + constexpr static NoOperators + create(int) + { return NoOperators(); } +}; + +// std defined ordering types are expected +template +struct ReturnOrd +{ + constexpr static Ord + create(int cmp) + { return cmp <=> 0; } +}; + +// However, other types that provide required +// operators are supported. +struct ReturnInt +{ + constexpr static int + create(int cmp) + { return cmp; } +}; + +struct CustomOrd +{ + constexpr static CustomOrd + create(int cmp) + { return CustomOrd(cmp); } + + CustomOrd() = default; + + explicit constexpr + CustomOrd(int cmp) + : v(cmp) {} + + friend constexpr bool + operator<(CustomOrd c, std::nullptr_t) + { return c.v < 0; } + + friend constexpr bool + operator<(std::nullptr_t, CustomOrd c) + { return 0 < c.v; } + + friend constexpr bool + operator>(CustomOrd c, std::nullptr_t) + { return c.v > 0; } + + friend constexpr bool + operator>(std::nullptr_t, CustomOrd c) + { return 0 > c.v; } + + friend constexpr bool + operator<=(CustomOrd c, std::nullptr_t) + { return c.v <= 0; } + + friend constexpr bool + operator<=(std::nullptr_t, CustomOrd c) + { return 0 <= c.v; } + + friend constexpr bool + operator>=(CustomOrd c, std::nullptr_t) + { return c.v >= 0; } + + friend constexpr bool + operator>=(std::nullptr_t, CustomOrd c) + { return 0 >= c.v; } + + friend constexpr CustomOrd + operator<=>(CustomOrd c, std::nullptr_t) + { return c; } + + friend constexpr CustomOrd + operator<=>(std::nullptr_t, CustomOrd c) + { return CustomOrd(-c.v); } + +private: + int v = 0; +}; + +template +void +test_relational(bool use_overloaded) +{ + CStr1 cs1{s1}; CStr2 cs2{s2}; + + if (use_overloaded) + { + // Overloaded operaetors compare content of the string, + // and cs1 > cs2; + + VERIFY( !(cs1 < cs2) ); + VERIFY( !std::less<>{}(cs1, cs2) ); + VERIFY( !std::ranges::less{}(cs1, cs2) ); + + VERIFY( (cs1 > cs2) ); + VERIFY( std::greater<>{}(cs1, cs2) ); + VERIFY( std::ranges::greater{}(cs1, cs2) ); + + VERIFY( !(cs1 < cs2) ); + VERIFY( !std::less_equal<>{}(cs1, cs2) ); + VERIFY( !std::ranges::less_equal{}(cs1, cs2) ); + + VERIFY( (cs1 > cs2) ); + VERIFY( std::greater_equal<>{}(cs1, cs2) ); + VERIFY( std::ranges::greater_equal{}(cs1, cs2) ); + } + else + { + // Without overloaded operators, we comapre pointers, + // and cs1 < cs2; + + VERIFY( (cs1 < cs2) ); + VERIFY( std::less<>{}(cs1, cs2) ); + VERIFY( std::ranges::less{}(cs1, cs2) ); + + VERIFY( !(cs1 > cs2) ); + VERIFY( !std::greater<>{}(cs1, cs2) ); + VERIFY( !std::ranges::greater{}(cs1, cs2) ); + + VERIFY( (cs1 < cs2) ); + VERIFY( std::less_equal<>{}(cs1, cs2) ); + VERIFY( std::ranges::less_equal{}(cs1, cs2) ); + + VERIFY( !(cs1 > cs2) ); + VERIFY( !std::greater_equal<>{}(cs1, cs2) ); + VERIFY( !std::ranges::greater_equal{}(cs1, cs2) ); + } +} + +template +void +test_relational_type(bool use_overloaded) +{ + test_relational(use_overloaded); + test_relational(use_overloaded); + test_relational(use_overloaded); +} + +template +void +test_relational_return() +{ + test_relational_type>(true); + test_relational_type>(true); + test_relational_type>(true); + test_relational, CStrFree>(true); + test_relational, CStrMixed>(true); +} + +template +void +test_spaceship(bool use_overloaded) +{ + CStr1 cs1{s1}; CStr2 cs2{s2}; + + if (use_overloaded) + { + // Overloaded operaetors compare content of the string, + // and cs1 > cs2; + VERIFY( (cs1 <=> cs2) > 0 ); + VERIFY( std::compare_three_way{}(cs1, cs2) > 0 ); + } + else + { + // Without overloaded operators, we comapre pointers, + // and cs1 < cs2; + VERIFY( (cs1 <=> cs2) < 0 ); + VERIFY( std::compare_three_way{}(cs1, cs2) < 0 ); + } +} + +template +void +test_spaceship_type(bool use_overloaded) +{ + test_spaceship(use_overloaded); + test_spaceship(use_overloaded); + test_spaceship(use_overloaded); +} + +template +void +test_std_ordering() +{ + using RC = ReturnOrd; + test_relational_return(); + test_spaceship_type>(true); + test_spaceship_type>(true); + test_spaceship_type>(true); + test_spaceship, CStrFree>(true); + test_spaceship, CStrMixed>(true); +} + +template +void +test_no_relational() +{ + CStr1 c1{}; CStr2 c2{}; + static_assert(!requires { c1 < c2; }); + static_assert(!requires { c1 > c2; }); + static_assert(!requires { c1 <= c2; }); + static_assert(!requires { c1 >= c2; }); +} + +template +void +test_no_relational_type() +{ + test_no_relational(); + test_no_relational(); + test_no_relational(); +} + +template +void +test_no_relational_return() +{ + test_no_relational_type>(); + test_no_relational_type>(); + test_no_relational_type>(); + test_no_relational, CStrFree>(); + test_no_relational, CStrMixed>(); +} + +int main() +{ + test_std_ordering(); + test_std_ordering(); + test_std_ordering(); + + test_relational_type(false); + test_relational_return(); + test_relational_return(); + + test_no_relational_return(); + test_no_relational_return(); +}