Tested x86_64-linux. Pushed to trunk. -- >8 --
Implement the changes from P2944R3 which add constraints to the comparison operators of std::pair, std::tuple, and std::variant. The paper also changes std::optional, but we already constrain its comparisons using SFINAE on the return type. However, we need some additional constraints on the [optional.comp.with.t] operators that compare an optional with a value. The paper doesn't say to do that, but I think it's needed because otherwise when the comparison for two optional objects fails its constraints, the two overloads that are supposed to be for comparing to a non-optional become the best overload candidates, but are ambiguous (and we don't even get as far as checking the constraints for satisfaction). I reported LWG 4072 for this. The paper does not change std::expected, but probably should have done. I'll submit an LWG issue about that and implement it separately. Also add [[nodiscard]] to all these comparison operators. libstdc++-v3/ChangeLog: * include/bits/stl_pair.h (operator==): Add constraint. * include/bits/version.def (constrained_equality): Define. * include/bits/version.h: Regenerate. * include/std/optional: Define feature test macro. (__optional_rep_op_t): Use is_convertible_v instead of is_convertible. * include/std/tuple: Define feature test macro. (operator==, __tuple_cmp, operator<=>): Reimplement C++20 comparisons using lambdas. Add constraints. * include/std/utility: Define feature test macro. * include/std/variant: Define feature test macro. (_VARIANT_RELATION_FUNCTION_TEMPLATE): Add constraints. (variant): Remove unnecessary friend declarations for comparison operators. * testsuite/20_util/optional/relops/constrained.cc: New test. * testsuite/20_util/pair/comparison_operators/constrained.cc: New test. * testsuite/20_util/tuple/comparison_operators/constrained.cc: New test. * testsuite/20_util/variant/relops/constrained.cc: New test. * testsuite/20_util/tuple/comparison_operators/overloaded.cc: Disable for C++20 and later. * testsuite/20_util/tuple/comparison_operators/overloaded2.cc: Remove dg-error line for target c++20. --- libstdc++-v3/include/bits/stl_pair.h | 16 +- libstdc++-v3/include/bits/version.def | 9 + libstdc++-v3/include/bits/version.h | 10 + libstdc++-v3/include/std/optional | 50 +++- libstdc++-v3/include/std/tuple | 102 ++++--- libstdc++-v3/include/std/utility | 1 + libstdc++-v3/include/std/variant | 28 +- .../20_util/optional/relops/constrained.cc | 258 ++++++++++++++++++ .../pair/comparison_operators/constrained.cc | 48 ++++ .../tuple/comparison_operators/constrained.cc | 50 ++++ .../tuple/comparison_operators/overloaded.cc | 6 +- .../tuple/comparison_operators/overloaded2.cc | 1 - .../20_util/variant/relops/constrained.cc | 175 ++++++++++++ 13 files changed, 679 insertions(+), 75 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc create mode 100644 libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc create mode 100644 libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc create mode 100644 libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h index 45317417c9c..0c1e5719a1a 100644 --- a/libstdc++-v3/include/bits/stl_pair.h +++ b/libstdc++-v3/include/bits/stl_pair.h @@ -1000,14 +1000,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _T1, typename _T2> pair(_T1, _T2) -> pair<_T1, _T2>; #endif -#if __cpp_lib_three_way_comparison && __cpp_lib_concepts +#if __cpp_lib_three_way_comparison // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3865. Sorting a range of pairs /// Two pairs are equal iff their members are equal. template<typename _T1, typename _T2, typename _U1, typename _U2> - inline _GLIBCXX_CONSTEXPR bool + [[nodiscard]] + constexpr bool operator==(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y) + requires requires { + { __x.first == __y.first } -> __detail::__boolean_testable; + { __x.second == __y.second } -> __detail::__boolean_testable; + } { return __x.first == __y.first && __x.second == __y.second; } /** Defines a lexicographical order for pairs. @@ -1018,6 +1023,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * less than `Q.second`. */ template<typename _T1, typename _T2, typename _U1, typename _U2> + [[nodiscard]] constexpr common_comparison_category_t<__detail::__synth3way_t<_T1, _U1>, __detail::__synth3way_t<_T2, _U2>> operator<=>(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y) @@ -1029,6 +1035,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #else /// Two pairs of the same type are equal iff their members are equal. template<typename _T1, typename _T2> + _GLIBCXX_NODISCARD inline _GLIBCXX_CONSTEXPR bool operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y) { return __x.first == __y.first && __x.second == __y.second; } @@ -1041,6 +1048,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * than `Q.second`. */ template<typename _T1, typename _T2> + _GLIBCXX_NODISCARD inline _GLIBCXX_CONSTEXPR bool operator<(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y) { return __x.first < __y.first @@ -1048,24 +1056,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Uses @c operator== to find the result. template<typename _T1, typename _T2> + _GLIBCXX_NODISCARD inline _GLIBCXX_CONSTEXPR bool operator!=(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y) { return !(__x == __y); } /// Uses @c operator< to find the result. template<typename _T1, typename _T2> + _GLIBCXX_NODISCARD inline _GLIBCXX_CONSTEXPR bool operator>(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y) { return __y < __x; } /// Uses @c operator< to find the result. template<typename _T1, typename _T2> + _GLIBCXX_NODISCARD inline _GLIBCXX_CONSTEXPR bool operator<=(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y) { return !(__y < __x); } /// Uses @c operator< to find the result. template<typename _T1, typename _T2> + _GLIBCXX_NODISCARD inline _GLIBCXX_CONSTEXPR bool operator>=(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y) { return !(__x < __y); } diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 5c0477fb61e..f0ba4f2bb3d 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1237,6 +1237,15 @@ ftms = { }; }; +ftms = { + name = constrained_equality; + values = { + v = 202403; + cxxmin = 20; + extra_cond = "__glibcxx_three_way_comparison"; + }; +}; + ftms = { name = erase_if; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 65e708c73fb..f30f51dcedc 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1373,6 +1373,16 @@ #endif /* !defined(__cpp_lib_constexpr_vector) && defined(__glibcxx_want_constexpr_vector) */ #undef __glibcxx_want_constexpr_vector +#if !defined(__cpp_lib_constrained_equality) +# if (__cplusplus >= 202002L) && (__glibcxx_three_way_comparison) +# define __glibcxx_constrained_equality 202403L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_constrained_equality) +# define __cpp_lib_constrained_equality 202403L +# endif +# endif +#endif /* !defined(__cpp_lib_constrained_equality) && defined(__glibcxx_want_constrained_equality) */ +#undef __glibcxx_want_constrained_equality + #if !defined(__cpp_lib_erase_if) # if (__cplusplus >= 202002L) && _GLIBCXX_HOSTED # define __glibcxx_erase_if 202002L diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index 3507c36a4d8..48e0f3d36f2 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -34,6 +34,7 @@ #define __glibcxx_want_freestanding_optional #define __glibcxx_want_optional +#define __glibcxx_want_constrained_equality #include <bits/version.h> #ifdef __cpp_lib_optional // C++ >= 17 @@ -1194,7 +1195,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> using __optional_relop_t = - enable_if_t<is_convertible<_Tp, bool>::value, bool>; + enable_if_t<is_convertible_v<_Tp, bool>, bool>; template<typename _Tp, typename _Up> using __optional_eq_t = __optional_relop_t< @@ -1279,6 +1280,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #ifdef __cpp_lib_three_way_comparison template<typename _Tp, three_way_comparable_with<_Tp> _Up> + [[nodiscard]] constexpr compare_three_way_result_t<_Tp, _Up> operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) { @@ -1288,12 +1290,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Comparisons with nullopt. template<typename _Tp> + [[nodiscard]] constexpr bool operator==(const optional<_Tp>& __lhs, nullopt_t) noexcept { return !__lhs; } #ifdef __cpp_lib_three_way_comparison template<typename _Tp> + [[nodiscard]] constexpr strong_ordering operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept { return bool(__x) <=> false; } @@ -1354,76 +1358,96 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return !__rhs; } #endif // three-way-comparison + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4072. std::optional comparisons: constrain harder +#if __cpp_lib_concepts +# define _REQUIRES_NOT_OPTIONAL(T) requires (!__is_optional_v<T>) +#else +# define _REQUIRES_NOT_OPTIONAL(T) +#endif + // Comparisons with value type. template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator==(const optional<_Tp>& __lhs, const _Up& __rhs) + operator== [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs) -> __optional_eq_t<_Tp, _Up> { return __lhs && *__lhs == __rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto operator==(const _Up& __lhs, const optional<_Tp>& __rhs) -> __optional_eq_t<_Up, _Tp> { return __rhs && __lhs == *__rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator!=(const optional<_Tp>& __lhs, const _Up& __rhs) + operator!= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs) -> __optional_ne_t<_Tp, _Up> { return !__lhs || *__lhs != __rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator!=(const _Up& __lhs, const optional<_Tp>& __rhs) + operator!= [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs) -> __optional_ne_t<_Up, _Tp> { return !__rhs || __lhs != *__rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator<(const optional<_Tp>& __lhs, const _Up& __rhs) + operator< [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs) -> __optional_lt_t<_Tp, _Up> { return !__lhs || *__lhs < __rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator<(const _Up& __lhs, const optional<_Tp>& __rhs) + operator< [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs) -> __optional_lt_t<_Up, _Tp> { return __rhs && __lhs < *__rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator>(const optional<_Tp>& __lhs, const _Up& __rhs) + operator> [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs) -> __optional_gt_t<_Tp, _Up> { return __lhs && *__lhs > __rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator>(const _Up& __lhs, const optional<_Tp>& __rhs) + operator> [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs) -> __optional_gt_t<_Up, _Tp> { return !__rhs || __lhs > *__rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator<=(const optional<_Tp>& __lhs, const _Up& __rhs) + operator<= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs) -> __optional_le_t<_Tp, _Up> { return !__lhs || *__lhs <= __rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator<=(const _Up& __lhs, const optional<_Tp>& __rhs) + operator<= [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs) -> __optional_le_t<_Up, _Tp> { return __rhs && __lhs <= *__rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator>=(const optional<_Tp>& __lhs, const _Up& __rhs) + operator>= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs) -> __optional_ge_t<_Tp, _Up> { return __lhs && *__lhs >= __rhs; } template<typename _Tp, typename _Up> + _REQUIRES_NOT_OPTIONAL(_Up) constexpr auto - operator>=(const _Up& __lhs, const optional<_Tp>& __rhs) + operator>= [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs) -> __optional_ge_t<_Up, _Tp> { return !__rhs || __lhs >= *__rhs; } @@ -1432,7 +1456,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION requires (!__is_optional_v<_Up>) && three_way_comparable_with<_Up, _Tp> constexpr compare_three_way_result_t<_Tp, _Up> - operator<=>(const optional<_Tp>& __x, const _Up& __v) + operator<=> [[nodiscard]] (const optional<_Tp>& __x, const _Up& __v) { return bool(__x) ? *__x <=> __v : strong_ordering::less; } #endif diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 3065058e184..df3f6e38eeb 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -51,6 +51,7 @@ #define __glibcxx_want_make_from_tuple #define __glibcxx_want_ranges_zip #define __glibcxx_want_tuple_like +#define __glibcxx_want_constrained_equality #include <bits/version.h> namespace std _GLIBCXX_VISIBILITY(default) @@ -250,17 +251,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cpp_lib_tuple_like // >= C++23 struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = default; }; - // These forward declarations are used by the operator<=> overload for + // This forward declaration is used by the operator<=> overload for // tuple-like types. - template<typename _Cat, typename _Tp, typename _Up> + template<typename _Cat, typename _Tp, typename _Up, typename _IndexSeq> constexpr _Cat - __tuple_cmp(const _Tp&, const _Up&, index_sequence<>); - - template<typename _Cat, typename _Tp, typename _Up, - size_t _Idx0, size_t... _Idxs> - constexpr _Cat - __tuple_cmp(const _Tp& __t, const _Up& __u, - index_sequence<_Idx0, _Idxs...>); + __tuple_cmp(const _Tp& __t, const _Up& __u, _IndexSeq); #endif // C++23 /** @@ -1848,7 +1843,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<__tuple_like _UTuple> requires (!__is_tuple_v<_UTuple>) friend constexpr bool - operator==(const tuple& __t, const _UTuple& __u) + operator== [[nodiscard]] (const tuple& __t, const _UTuple& __u) { static_assert(sizeof...(_Elements) == tuple_size_v<_UTuple>, "tuple objects can only be compared if they have equal sizes."); @@ -2521,6 +2516,58 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif +#if __cpp_lib_three_way_comparison + template<typename... _Tps, typename... _Ups> + requires (sizeof...(_Tps) == sizeof...(_Ups)) + && (requires (const _Tps& __t, const _Ups& __u) { + { __t == __u } -> __detail::__boolean_testable; + } && ...) + constexpr bool + operator== [[nodiscard]] (const tuple<_Tps...>& __t, + const tuple<_Ups...>& __u) + { + return [&]<size_t... _Inds>(index_sequence<_Inds...>) { + // Fold == over the tuples until non-equal elements are found. + return ((std::get<_Inds>(__t) == std::get<_Inds>(__u)) && ...); + }(index_sequence_for<_Tps...>{}); + } + + template<typename _Cat, typename _Tp, typename _Up, typename _IndexSeq> + [[nodiscard]] + constexpr _Cat + __tuple_cmp(const _Tp& __t, const _Up& __u, _IndexSeq __indices) + { + _Cat __c = _Cat::equivalent; + + // Set __c to the comparison result of two corresponding elements. + // Return true they are equivalent. + auto __cmp = [&]<size_t _Ind>(integral_constant<size_t, _Ind>) { + __c = __detail::__synth3way(std::get<_Ind>(__t), std::get<_Ind>(__u)); + return __c == 0; + }; + + [&]<size_t... _Inds>(index_sequence<_Inds...>) { + // Fold __cmp over the tuples until non-equivalent elements are found. + (void)(__cmp(integral_constant<size_t, _Inds>{}) && ...); + }(__indices); + + return __c; + } + + template<typename... _Tps, typename... _Ups> + requires (sizeof...(_Tps) == sizeof...(_Ups)) + && (requires { typename __detail::__synth3way_t<_Tps, _Ups>; } && ...) + constexpr + common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...> + operator<=> [[nodiscard]] (const tuple<_Tps...>& __t, + const tuple<_Ups...>& __u) + { + using _Cat + = common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>; + return std::__tuple_cmp<_Cat>(__t, __u, index_sequence_for<_Tps...>()); + } +#else + // This class performs the comparison operations on tuples template<typename _Tp, typename _Up, size_t __i, size_t __size> struct __tuple_compare @@ -2552,6 +2599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator==(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) @@ -2564,36 +2612,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __compare::__eq(__t, __u); } -#if __cpp_lib_three_way_comparison - template<typename _Cat, typename _Tp, typename _Up> - constexpr _Cat - __tuple_cmp(const _Tp&, const _Up&, index_sequence<>) - { return _Cat::equivalent; } - - template<typename _Cat, typename _Tp, typename _Up, - size_t _Idx0, size_t... _Idxs> - constexpr _Cat - __tuple_cmp(const _Tp& __t, const _Up& __u, - index_sequence<_Idx0, _Idxs...>) - { - auto __c - = __detail::__synth3way(std::get<_Idx0>(__t), std::get<_Idx0>(__u)); - if (__c != 0) - return __c; - return std::__tuple_cmp<_Cat>(__t, __u, index_sequence<_Idxs...>()); - } - - template<typename... _Tps, typename... _Ups> - constexpr - common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...> - operator<=>(const tuple<_Tps...>& __t, const tuple<_Ups...>& __u) - { - using _Cat - = common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>; - return std::__tuple_cmp<_Cat>(__t, __u, index_sequence_for<_Tps...>()); - } -#else template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator<(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) @@ -2607,24 +2627,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator!=(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) { return !(__t == __u); } template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator>(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) { return __u < __t; } template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator<=(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) { return !(__u < __t); } template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator>=(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) diff --git a/libstdc++-v3/include/std/utility b/libstdc++-v3/include/std/utility index 212513f6f48..56467160a2f 100644 --- a/libstdc++-v3/include/std/utility +++ b/libstdc++-v3/include/std/utility @@ -93,6 +93,7 @@ #define __glibcxx_want_tuples_by_type #define __glibcxx_want_unreachable #define __glibcxx_want_tuple_like +#define __glibcxx_want_constrained_equality #include <bits/version.h> namespace std _GLIBCXX_VISIBILITY(default) diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 748e9bae1cb..8072e1f17a8 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -33,6 +33,7 @@ #define __glibcxx_want_freestanding_variant #define __glibcxx_want_variant +#define __glibcxx_want_constrained_equality #include <bits/version.h> #ifdef __cpp_lib_variant // C++ >= 17 @@ -1236,9 +1237,19 @@ namespace __variant struct monostate { }; +#if __cpp_lib_concepts +# define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP) \ + requires ((requires (const TYPES& __t) { \ + { __t OP __t } -> __detail::__boolean_testable; }) && ...) +#else +# define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP) +#endif + #define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP, __NAME) \ template<typename... _Types> \ - constexpr bool operator __OP(const variant<_Types...>& __lhs, \ + _VARIANT_RELATION_FUNCTION_CONSTRAINTS(_Types, __OP) \ + constexpr bool \ + operator __OP [[nodiscard]] (const variant<_Types...>& __lhs, \ const variant<_Types...>& __rhs) \ { \ bool __ret = true; \ @@ -1690,21 +1701,6 @@ namespace __variant template<size_t _Np, typename _Vp> friend constexpr decltype(auto) __detail::__variant::__get(_Vp&& __v) noexcept; - -#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP) \ - template<typename... _Tp> \ - friend constexpr bool \ - operator __OP(const variant<_Tp...>& __lhs, \ - const variant<_Tp...>& __rhs); - - _VARIANT_RELATION_FUNCTION_TEMPLATE(<) - _VARIANT_RELATION_FUNCTION_TEMPLATE(<=) - _VARIANT_RELATION_FUNCTION_TEMPLATE(==) - _VARIANT_RELATION_FUNCTION_TEMPLATE(!=) - _VARIANT_RELATION_FUNCTION_TEMPLATE(>=) - _VARIANT_RELATION_FUNCTION_TEMPLATE(>) - -#undef _VARIANT_RELATION_FUNCTION_TEMPLATE }; template<size_t _Np, typename... _Types> diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc new file mode 100644 index 00000000000..0e325618008 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc @@ -0,0 +1,258 @@ +// { dg-do compile { target c++20 } } + +#include <optional> + +#ifndef __cpp_lib_constrained_equality +# error "Feature-test macro for constrained_equality missing" +#elif __cpp_lib_constrained_equality != 202403 +# error "Feature-test macro for constrained_equality has wrong value" +#endif + +template<typename T, typename U = T> +concept eq_comparable += requires (const std::optional<T>& t, const std::optional<U>& u) { + t == u; + *t == u; + t == *u; +}; + +template<typename T, typename U = T> +concept ne_comparable += requires (const std::optional<T>& t, const std::optional<U>& u) { + t != u; + *t != u; + t != *u; +}; + +template<typename T, typename U = T> +concept lt_comparable += requires (const std::optional<T>& t, const std::optional<U>& u) { + t < u; + *t < u; + t < *u; +}; + +template<typename T, typename U = T> +concept le_comparable += requires (const std::optional<T>& t, const std::optional<U>& u) { + t <= u; + *t <= u; + t <= *u; +}; + +template<typename T, typename U = T> +concept gt_comparable += requires (const std::optional<T>& t, const std::optional<U>& u) { + t > u; + *t > u; + t > *u; +}; + +template<typename T, typename U = T> +concept ge_comparable += requires (const std::optional<T>& t, const std::optional<U>& u) { + t >= u; + *t >= u; + t >= *u; +}; + +static_assert( eq_comparable<int> ); +static_assert( ne_comparable<int> ); +static_assert( lt_comparable<int> ); +static_assert( le_comparable<int> ); +static_assert( gt_comparable<int> ); +static_assert( ge_comparable<int> ); +static_assert( eq_comparable<int, long> ); +static_assert( ne_comparable<int, long> ); +static_assert( lt_comparable<int, long> ); +static_assert( le_comparable<int, long> ); +static_assert( gt_comparable<int, long> ); +static_assert( ge_comparable<int, long> ); +static_assert( eq_comparable<short, int> ); +static_assert( ne_comparable<short, int> ); +static_assert( lt_comparable<short, int> ); +static_assert( le_comparable<short, int> ); +static_assert( gt_comparable<short, int> ); +static_assert( ge_comparable<short, int> ); + +struct A { }; +static_assert( ! eq_comparable<A> ); +static_assert( ! ne_comparable<A> ); +static_assert( ! lt_comparable<A> ); +static_assert( ! le_comparable<A> ); +static_assert( ! gt_comparable<A> ); +static_assert( ! ge_comparable<A> ); +static_assert( ! eq_comparable<A, A> ); +static_assert( ! ne_comparable<A, A> ); +static_assert( ! lt_comparable<A, A> ); +static_assert( ! le_comparable<A, A> ); +static_assert( ! gt_comparable<A, A> ); +static_assert( ! ge_comparable<A, A> ); +static_assert( ! eq_comparable<A, int> ); +static_assert( ! ne_comparable<A, int> ); +static_assert( ! lt_comparable<A, int> ); +static_assert( ! le_comparable<A, int> ); +static_assert( ! gt_comparable<A, int> ); +static_assert( ! ge_comparable<A, int> ); + +struct B { }; +void operator==(B, B); +static_assert( ! eq_comparable<B> ); +static_assert( ! ne_comparable<B> ); +static_assert( ! lt_comparable<B> ); +static_assert( ! le_comparable<B> ); +static_assert( ! gt_comparable<B> ); +static_assert( ! ge_comparable<B> ); +static_assert( ! eq_comparable<B, B> ); +static_assert( ! ne_comparable<B, B> ); +static_assert( ! lt_comparable<B, B> ); +static_assert( ! le_comparable<B, B> ); +static_assert( ! gt_comparable<B, B> ); +static_assert( ! ge_comparable<B, B> ); +static_assert( ! eq_comparable<B, int> ); +static_assert( ! ne_comparable<B, int> ); +static_assert( ! lt_comparable<B, int> ); +static_assert( ! le_comparable<B, int> ); +static_assert( ! gt_comparable<B, int> ); +static_assert( ! ge_comparable<B, int> ); + +struct C { }; +bool operator==(C, C); +static_assert( eq_comparable<C> ); +static_assert( ne_comparable<C> ); +static_assert( ! lt_comparable<C> ); +static_assert( ! le_comparable<C> ); +static_assert( ! gt_comparable<C> ); +static_assert( ! ge_comparable<C> ); +static_assert( eq_comparable<C, C> ); +static_assert( ne_comparable<C, C> ); +static_assert( eq_comparable<C, C> ); +static_assert( ne_comparable<C, C> ); + +struct D { }; +int operator==(D, D); +bool operator!=(D, D) = delete; +static_assert( eq_comparable<D> ); +static_assert( ! ne_comparable<D> ); +static_assert( ! lt_comparable<D> ); +static_assert( ! le_comparable<D> ); +static_assert( ! gt_comparable<D> ); +static_assert( ! ge_comparable<D> ); +static_assert( eq_comparable<D, D> ); +static_assert( ! ne_comparable<D, D> ); +static_assert( eq_comparable<D, D> ); +static_assert( ! ne_comparable<D, D> ); + +struct E { }; +bool operator==(/* not-const */ E&, const E&); +static_assert( ! eq_comparable<E> ); +static_assert( ! ne_comparable<E> ); +static_assert( ! lt_comparable<E> ); +static_assert( ! le_comparable<E> ); +static_assert( ! gt_comparable<E> ); +static_assert( ! ge_comparable<E> ); +static_assert( ! eq_comparable<E, E> ); +static_assert( ! eq_comparable<E, E> ); + +struct F { }; +bool operator<(F, F); +void operator>(F, F); +static_assert( ! eq_comparable<F> ); +static_assert( ! ne_comparable<F> ); +static_assert( lt_comparable<F> ); +static_assert( ! le_comparable<F> ); +static_assert( ! gt_comparable<F> ); +static_assert( ! ge_comparable<F> ); +static_assert( lt_comparable<F, F> ); +static_assert( lt_comparable<F, F> ); + +struct G { }; +bool operator<=(G, G); +void operator<(G, G); +static_assert( ! eq_comparable<G> ); +static_assert( ! ne_comparable<G> ); +static_assert( ! lt_comparable<G> ); +static_assert( le_comparable<G> ); +static_assert( ! gt_comparable<G> ); +static_assert( ! ge_comparable<G> ); +static_assert( le_comparable<G, G> ); +static_assert( le_comparable<G, G> ); + +struct H { }; +bool operator>(H, H); +void operator>=(H, H); +static_assert( ! eq_comparable<H> ); +static_assert( ! ne_comparable<H> ); +static_assert( ! lt_comparable<H> ); +static_assert( ! le_comparable<H> ); +static_assert( gt_comparable<H> ); +static_assert( ! ge_comparable<H> ); +static_assert( gt_comparable<H, H> ); +static_assert( gt_comparable<H, H> ); + +struct I { }; +bool operator>=(I, I); +void operator<=(I, I); +static_assert( ! eq_comparable<I> ); +static_assert( ! ne_comparable<I> ); +static_assert( ! lt_comparable<I> ); +static_assert( ! le_comparable<I> ); +static_assert( ! gt_comparable<I> ); +static_assert( ge_comparable<I> ); +static_assert( ge_comparable<I, I> ); +static_assert( ge_comparable<I, I> ); + +struct J { }; +bool operator==(J, J); +std::weak_ordering operator<=>(J, J); +static_assert( eq_comparable<J> ); +static_assert( ne_comparable<J> ); +static_assert( lt_comparable<J> ); +static_assert( le_comparable<J> ); +static_assert( gt_comparable<J> ); +static_assert( ge_comparable<J> ); + +struct K { }; +int operator==(K, K); // non-bool prevents synthesis of != +void operator<=(K, K); +std::weak_ordering operator<=>(K, K); +static_assert( eq_comparable<K> ); +static_assert( ! ne_comparable<K> ); +static_assert( lt_comparable<K> ); +static_assert( ! le_comparable<K> ); +static_assert( gt_comparable<K> ); +static_assert( ge_comparable<K> ); + +bool operator==(A, B); +static_assert( eq_comparable<A, B> ); +static_assert( eq_comparable<B, A> ); +static_assert( ne_comparable<A, B> ); +static_assert( ne_comparable<B, A> ); +static_assert( ! lt_comparable<A, B> ); +static_assert( ! le_comparable<A, B> ); +static_assert( ! gt_comparable<A, B> ); +static_assert( ! ge_comparable<A, B> ); + +int operator==(C, D); // non-bool prevents synthesis of != and reversed args +static_assert( eq_comparable<C, D> ); +static_assert( ! eq_comparable<D, C> ); +static_assert( ! ne_comparable<C, D> ); +static_assert( ! ne_comparable<D, C> ); +static_assert( ! lt_comparable<C, D> ); +static_assert( ! le_comparable<C, D> ); +static_assert( ! gt_comparable<C, D> ); +static_assert( ! ge_comparable<C, D> ); + +std::weak_ordering operator<=>(E, F); +static_assert( ! eq_comparable<E, F> ); +static_assert( ! eq_comparable<F, E> ); +static_assert( ! ne_comparable<E, F> ); +static_assert( ! ne_comparable<F, E> ); +static_assert( lt_comparable<E, F> ); +static_assert( le_comparable<E, F> ); +static_assert( gt_comparable<E, F> ); +static_assert( ge_comparable<E, F> ); +static_assert( lt_comparable<F, E> ); +static_assert( le_comparable<F, E> ); +static_assert( gt_comparable<F, E> ); +static_assert( ge_comparable<F, E> ); diff --git a/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc new file mode 100644 index 00000000000..a35dbd265a7 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc @@ -0,0 +1,48 @@ +// { dg-do compile { target c++20 } } + +#include <utility> + +#ifndef __cpp_lib_constrained_equality +# error "Feature-test macro for constrained_equality missing" +#elif __cpp_lib_constrained_equality != 202403 +# error "Feature-test macro for constrained_equality has wrong value" +#endif + +template<typename T> +concept equality_comparable = requires (const T& t) { t == t; t != t; }; + +static_assert( equality_comparable<std::pair<int, long>> ); + +struct A { }; +static_assert( ! equality_comparable<std::pair<A, long>> ); +static_assert( ! equality_comparable<std::pair<int, A>> ); +static_assert( ! equality_comparable<std::pair<A, A>> ); + +struct B { }; +void operator==(B, B); +static_assert( ! equality_comparable<std::pair<B, long>> ); +static_assert( ! equality_comparable<std::pair<int, B>> ); +static_assert( ! equality_comparable<std::pair<B, B>> ); +static_assert( ! equality_comparable<std::pair<A, B>> ); + +struct C { }; +int operator==(C, C); +static_assert( equality_comparable<std::pair<C, long>> ); +static_assert( equality_comparable<std::pair<int, C>> ); +static_assert( equality_comparable<std::pair<C, C>> ); +static_assert( ! equality_comparable<std::pair<A, C>> ); + +struct D { }; +bool operator==(D, D); +bool operator!=(D, D) = delete; +static_assert( equality_comparable<std::pair<D, long>> ); +static_assert( equality_comparable<std::pair<int, D>> ); +static_assert( equality_comparable<std::pair<D, D>> ); +static_assert( equality_comparable<std::pair<C, D>> ); +static_assert( ! equality_comparable<std::pair<A, C>> ); + +struct E { }; +bool operator==(/* not-const */ E&, const E&); +static_assert( ! equality_comparable<std::pair<E, long>> ); +static_assert( ! equality_comparable<std::pair<int, E>> ); +static_assert( ! equality_comparable<std::pair<E, E>> ); diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc new file mode 100644 index 00000000000..47035ab18ba --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc @@ -0,0 +1,50 @@ +// { dg-do compile { target c++20 } } + +#include <tuple> + +#ifndef __cpp_lib_constrained_equality +# error "Feature-test macro for constrained_equality missing" +#elif __cpp_lib_constrained_equality != 202403 +# error "Feature-test macro for constrained_equality has wrong value" +#endif + +template<typename T> +concept equality_comparable = requires (const T& t) { t == t; t != t; }; + +static_assert( equality_comparable<std::tuple<>> ); +static_assert( equality_comparable<std::tuple<int>> ); +static_assert( equality_comparable<std::tuple<int, long>> ); +static_assert( equality_comparable<std::tuple<int, long, short>> ); + +struct A { }; +static_assert( ! equality_comparable<std::tuple<A>> ); +static_assert( ! equality_comparable<std::tuple<int, A>> ); +static_assert( ! equality_comparable<std::tuple<A, A>> ); + +struct B { }; +void operator==(B, B); +static_assert( ! equality_comparable<std::tuple<B>> ); +static_assert( ! equality_comparable<std::tuple<int, B>> ); +static_assert( ! equality_comparable<std::tuple<B, B>> ); + +struct C { }; +int operator==(C, C); +static_assert( equality_comparable<std::tuple<C>> ); +static_assert( equality_comparable<std::tuple<int, C>> ); +static_assert( equality_comparable<std::tuple<C, C>> ); +static_assert( ! equality_comparable<std::tuple<A, C>> ); + +struct D { }; +bool operator==(D, D); +bool operator!=(D, D) = delete; +static_assert( equality_comparable<std::tuple<D>> ); +static_assert( equality_comparable<std::tuple<int, D>> ); +static_assert( equality_comparable<std::tuple<D, D>> ); +static_assert( equality_comparable<std::tuple<C, D>> ); +static_assert( ! equality_comparable<std::tuple<A, C>> ); + +struct E { }; +bool operator==(/* not-const */ E&, const E&); +static_assert( ! equality_comparable<std::tuple<E>> ); +static_assert( ! equality_comparable<std::tuple<int, E>> ); +static_assert( ! equality_comparable<std::tuple<E, E>> ); diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc index 9f8a0f91785..7ae7f42d1a1 100644 --- a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc +++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc @@ -1,4 +1,5 @@ -// { dg-do compile { target c++11 } } +// { dg-do compile { target { c++11 && c++17_down } } } +// Not valid in C++20, because TwistedLogic doesn't model boolean-testable. // Copyright (C) 2014-2024 Free Software Foundation, Inc. // @@ -49,8 +50,5 @@ TwistedLogic operator<(const Compares&, const Compares&) { return {false}; } auto a = std::make_tuple(nullptr, Compares{}, 2, 'U'); auto b = a == a; -#if ! __cpp_lib_three_way_comparison -// Not valid in C++20, because TwistedLogic doesn't model boolean-testable. auto c = std::make_tuple("", Compares{}, 2, 'U'); auto d = c < c; -#endif diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc index 19617a5676e..8ea7ed0b797 100644 --- a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc +++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc @@ -50,5 +50,4 @@ auto a = std::make_tuple(nullptr, Compares{}, 2, 'U'); auto b = a < a; // { dg-error "no match for 'operator<'" "" { target c++20 } 0 } -// { dg-error "no match for .*_Synth3way|in requirements" "" { target c++20 } 0 } // { dg-error "ordered comparison" "" { target c++17_down } 0 } diff --git a/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc b/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc new file mode 100644 index 00000000000..95e8f754d1e --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc @@ -0,0 +1,175 @@ +// { dg-do compile { target c++20 } } + +#include <variant> + +#ifndef __cpp_lib_constrained_equality +# error "Feature-test macro for constrained_equality missing" +#elif __cpp_lib_constrained_equality != 202403 +# error "Feature-test macro for constrained_equality has wrong value" +#endif + +template<typename T, typename... U> +concept eq_comparable += requires (const std::variant<int, T, U...>& t) { t == t; }; + +template<typename T, typename... U> +concept ne_comparable += requires (const std::variant<int, T, U...>& t) { t != t; }; + +template<typename T, typename... U> +concept lt_comparable += requires (const std::variant<int, T, U...>& t) { t < t; }; + +template<typename T, typename... U> +concept le_comparable += requires (const std::variant<int, T, U...>& t) { t <= t; }; + +template<typename T, typename... U> +concept gt_comparable += requires (const std::variant<int, T, U...>& t) { t > t; }; + +template<typename T, typename... U> +concept ge_comparable += requires (const std::variant<int, T, U...>& t) { t >= t; }; + +static_assert( eq_comparable<int> ); +static_assert( ne_comparable<int> ); +static_assert( lt_comparable<int> ); +static_assert( le_comparable<int> ); +static_assert( gt_comparable<int> ); +static_assert( ge_comparable<int> ); +static_assert( eq_comparable<int, long> ); +static_assert( ne_comparable<int, long> ); +static_assert( lt_comparable<int, long> ); +static_assert( le_comparable<int, long> ); +static_assert( gt_comparable<int, long> ); +static_assert( ge_comparable<int, long> ); +static_assert( eq_comparable<short, int> ); +static_assert( ne_comparable<short, int> ); +static_assert( lt_comparable<short, int> ); +static_assert( le_comparable<short, int> ); +static_assert( gt_comparable<short, int> ); +static_assert( ge_comparable<short, int> ); +static_assert( eq_comparable<std::monostate, char, int> ); +static_assert( ne_comparable<std::monostate, char, int> ); +static_assert( lt_comparable<std::monostate, char, int> ); +static_assert( le_comparable<std::monostate, char, int> ); +static_assert( gt_comparable<std::monostate, char, int> ); +static_assert( ge_comparable<std::monostate, char, int> ); + +struct A { }; +static_assert( ! eq_comparable<A> ); +static_assert( ! ne_comparable<A> ); +static_assert( ! lt_comparable<A> ); +static_assert( ! le_comparable<A> ); +static_assert( ! gt_comparable<A> ); +static_assert( ! ge_comparable<A> ); + +struct B { }; +void operator==(B, B); +static_assert( ! eq_comparable<B> ); +static_assert( ! ne_comparable<B> ); +static_assert( ! lt_comparable<B> ); +static_assert( ! le_comparable<B> ); +static_assert( ! gt_comparable<B> ); +static_assert( ! ge_comparable<B> ); + +struct C { }; +bool operator==(C, C); +static_assert( eq_comparable<C> ); +static_assert( ne_comparable<C> ); +static_assert( ! lt_comparable<C> ); +static_assert( ! le_comparable<C> ); +static_assert( ! gt_comparable<C> ); +static_assert( ! ge_comparable<C> ); +static_assert( ne_comparable<C, C> ); + +struct D { }; +int operator==(D, D); // variant's operator== returns bool despite int here +bool operator!=(D, D) = delete; +static_assert( eq_comparable<D> ); +static_assert( ne_comparable<D> ); // variant's operator== can be used +static_assert( ! lt_comparable<D> ); +static_assert( ! le_comparable<D> ); +static_assert( ! gt_comparable<D> ); +static_assert( ! ge_comparable<D> ); + +struct E { }; +bool operator==(/* not-const */ E&, const E&); +static_assert( ! eq_comparable<E> ); +static_assert( ! ne_comparable<E> ); +static_assert( ! lt_comparable<E> ); +static_assert( ! le_comparable<E> ); +static_assert( ! gt_comparable<E> ); +static_assert( ! ge_comparable<E> ); + +struct F { }; +bool operator<(F, F); +void operator>(F, F); +static_assert( ! eq_comparable<F> ); +static_assert( ! ne_comparable<F> ); +static_assert( lt_comparable<F> ); +static_assert( ! le_comparable<F> ); +static_assert( ! gt_comparable<F> ); +static_assert( ! ge_comparable<F> ); + +struct G { }; +bool operator<=(G, G); +void operator<(G, G); +static_assert( ! eq_comparable<G> ); +static_assert( ! ne_comparable<G> ); +static_assert( ! lt_comparable<G> ); +static_assert( le_comparable<G> ); +static_assert( ! gt_comparable<G> ); +static_assert( ! ge_comparable<G> ); + +struct H { }; +bool operator>(H, H); +void operator>=(H, H); +static_assert( ! eq_comparable<H> ); +static_assert( ! ne_comparable<H> ); +static_assert( ! lt_comparable<H> ); +static_assert( ! le_comparable<H> ); +static_assert( gt_comparable<H> ); +static_assert( ! ge_comparable<H> ); +static_assert( gt_comparable<H, H> ); +static_assert( gt_comparable<H, H> ); + +struct I { }; +bool operator>=(I, I); +void operator<=(I, I); +static_assert( ! eq_comparable<I> ); +static_assert( ! ne_comparable<I> ); +static_assert( ! lt_comparable<I> ); +static_assert( ! le_comparable<I> ); +static_assert( ! gt_comparable<I> ); +static_assert( ge_comparable<I> ); +static_assert( ge_comparable<I, I> ); +static_assert( ge_comparable<I, I> ); + +struct J { }; +bool operator==(J, J); +std::weak_ordering operator<=>(J, J); +static_assert( eq_comparable<J> ); +static_assert( ne_comparable<J> ); +static_assert( lt_comparable<J> ); +static_assert( le_comparable<J> ); +static_assert( gt_comparable<J> ); +static_assert( ge_comparable<J> ); + +struct K { }; +int operator==(K, K); // variant's operator== returns bool despite int here +void operator<=(K, K); +std::weak_ordering operator<=>(K, K); +static_assert( eq_comparable<K> ); +static_assert( ne_comparable<K> ); // variant's operator== can be used +static_assert( lt_comparable<K> ); +static_assert( ! le_comparable<K> ); +static_assert( gt_comparable<K> ); +static_assert( ge_comparable<K> ); +static_assert( eq_comparable<K, J> ); +static_assert( ne_comparable<K, J> ); +static_assert( lt_comparable<K, J> ); +static_assert( ! le_comparable<K, J> ); +static_assert( gt_comparable<K, J> ); +static_assert( ge_comparable<K, J> ); -- 2.44.0