From 9e58988061f4175896de11af0caf9bdd48c9b046 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Fri, 21 Feb 2020 12:02:15 +0000 Subject: [PATCH] libstdc++: Define <=> for tuple, optional and variant Another piece of P1614R2. * include/std/optional (operator<=>(optional, optional)) (operator<=>(optional, nullopt), operator<=>(optional, U)): Define for C++20. * include/std/tuple (__tuple_cmp): New helper function for <=>. (operator<=>(tuple, tuple...)): Define for C++20. * include/std/variant (operator<=>(variant, variant)) (operator<=>(monostate, monostate)): Define for C++20. * testsuite/20_util/optional/relops/three_way.cc: New test. * testsuite/20_util/tuple/comparison_operators/three_way.cc: New test. * testsuite/20_util/variant/89851.cc: Move to ... * testsuite/20_util/variant/relops/89851.cc: ... here. * testsuite/20_util/variant/90008.cc: Move to ... * testsuite/20_util/variant/relops/90008.cc: ... here. * testsuite/20_util/variant/relops/three_way.cc: New test. --- libstdc++-v3/ChangeLog | 17 +++ libstdc++-v3/include/std/optional | 26 +++++ libstdc++-v3/include/std/tuple | 33 ++++++ libstdc++-v3/include/std/variant | 47 +++++++- .../20_util/optional/relops/three_way.cc | 76 +++++++++++++ .../tuple/comparison_operators/three_way.cc | 102 ++++++++++++++++++ .../20_util/variant/{ => relops}/89851.cc | 0 .../20_util/variant/{ => relops}/90008.cc | 0 .../20_util/variant/relops/three_way.cc | 54 ++++++++++ 9 files changed, 351 insertions(+), 4 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/optional/relops/three_way.cc create mode 100644 libstdc++-v3/testsuite/20_util/tuple/comparison_operators/three_way.cc rename libstdc++-v3/testsuite/20_util/variant/{ => relops}/89851.cc (100%) rename libstdc++-v3/testsuite/20_util/variant/{ => relops}/90008.cc (100%) create mode 100644 libstdc++-v3/testsuite/20_util/variant/relops/three_way.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 3649864644e..7e065ef3e1d 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,20 @@ +2020-02-21 Jonathan Wakely + + * include/std/optional (operator<=>(optional, optional)) + (operator<=>(optional, nullopt), operator<=>(optional, U)): + Define for C++20. + * include/std/tuple (__tuple_cmp): New helper function for <=>. + (operator<=>(tuple, tuple...)): Define for C++20. + * include/std/variant (operator<=>(variant, variant)) + (operator<=>(monostate, monostate)): Define for C++20. + * testsuite/20_util/optional/relops/three_way.cc: New test. + * testsuite/20_util/tuple/comparison_operators/three_way.cc: New test. + * testsuite/20_util/variant/89851.cc: Move to ... + * testsuite/20_util/variant/relops/89851.cc: ... here. + * testsuite/20_util/variant/90008.cc: Move to ... + * testsuite/20_util/variant/relops/90008.cc: ... here. + * testsuite/20_util/variant/relops/three_way.cc: New test. + 2020-02-20 Patrick Palka * include/std/ranges (views::__adaptor::__maybe_refwrap): New utility diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index b920a1453ba..37c2ba7a025 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -41,6 +41,9 @@ #include #include #include +#if __cplusplus > 201703L +# include +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -1027,12 +1030,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return !__rhs || (static_cast(__lhs) && *__lhs >= *__rhs); } +#ifdef __cpp_lib_three_way_comparison + template _Up> + constexpr compare_three_way_result_t<_Tp, _Up> + operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) + { + return __x && __y ? *__x <=> *__y : bool(__x) <=> bool(__y); + } +#endif + // Comparisons with nullopt. template constexpr bool operator==(const optional<_Tp>& __lhs, nullopt_t) noexcept { return !__lhs; } +#ifdef __cpp_lib_three_way_comparison + template + constexpr strong_ordering + operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept + { return bool(__x) <=> false; } +#else template constexpr bool operator==(nullopt_t, const optional<_Tp>& __rhs) noexcept @@ -1087,6 +1105,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr bool operator>=(nullopt_t, const optional<_Tp>& __rhs) noexcept { return !__rhs; } +#endif // three-way-comparison // Comparisons with value type. template @@ -1161,6 +1180,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION -> __optional_relop_t() >= declval<_Tp>())> { return !__rhs || __lhs >= *__rhs; } +#ifdef __cpp_lib_three_way_comparison + template + constexpr compare_three_way_result_t<_Tp, _Up> + operator<=>(const optional<_Tp>& __x, const _Up& __v) + { return bool(__x) ? *__x <=> __v : strong_ordering::less; } +#endif + // Swap and creation functions. // _GLIBCXX_RESOLVE_LIB_DEFECTS diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 3829d844e0b..808947781ae 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -39,6 +39,9 @@ #include #include #include +#if __cplusplus > 201703L +# include +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -1397,6 +1400,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __compare::__eq(__t, __u); } +#if __cpp_lib_three_way_comparison + template + constexpr _Cat + __tuple_cmp(const _Tp&, const _Up&, index_sequence<>) + { return _Cat::equivalent; } + + template + 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 + 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 constexpr bool operator<(const tuple<_TElements...>& __t, @@ -1433,6 +1465,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator>=(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) { return !(__t < __u); } +#endif // three_way_comparison // NB: DR 705. template diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 9568b718f53..258a5fb18bd 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -45,6 +45,9 @@ #include #include #include +#if __cplusplus > 201703L +# include +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -1181,10 +1184,7 @@ namespace __variant __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \ }, __rhs); \ return __ret; \ - } \ -\ - constexpr bool operator __OP(monostate, monostate) noexcept \ - { return 0 __OP 0; } + } _VARIANT_RELATION_FUNCTION_TEMPLATE(<, less) _VARIANT_RELATION_FUNCTION_TEMPLATE(<=, less_equal) @@ -1195,6 +1195,45 @@ namespace __variant #undef _VARIANT_RELATION_FUNCTION_TEMPLATE + constexpr bool operator==(monostate, monostate) noexcept { return true; } + +#ifdef __cpp_lib_three_way_comparison + template + requires (three_way_comparable<_Types> && ...) + constexpr + common_comparison_category_t...> + operator<=>(const variant<_Types...>& __v, const variant<_Types...>& __w) + { + common_comparison_category_t...> __ret + = strong_ordering::equal; + + __detail::__variant::__raw_idx_visit( + [&__ret, &__v] (auto&& __w_mem, auto __w_index) mutable + { + if constexpr (__w_index != variant_npos) + { + if (__v.index() == __w_index) + { + auto& __this_mem = std::get<__w_index>(__v); + __ret = __this_mem <=> __w_mem; + return; + } + } + __ret = (__v.index() + 1) <=> (__w_index + 1); + }, __w); + return __ret; + } + + constexpr strong_ordering + operator<=>(monostate, monostate) noexcept { return strong_ordering::equal; } +#else + constexpr bool operator!=(monostate, monostate) noexcept { return false; } + constexpr bool operator<(monostate, monostate) noexcept { return false; } + constexpr bool operator>(monostate, monostate) noexcept { return false; } + constexpr bool operator<=(monostate, monostate) noexcept { return true; } + constexpr bool operator>=(monostate, monostate) noexcept { return true; } +#endif + template constexpr decltype(auto) visit(_Visitor&&, _Variants&&...); diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/three_way.cc b/libstdc++-v3/testsuite/20_util/optional/relops/three_way.cc new file mode 100644 index 00000000000..d7bd16bf32a --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/relops/three_way.cc @@ -0,0 +1,76 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +void +test01() +{ + using O = std::optional; + + static_assert( std::is_eq(O{} <=> O{}) ); + static_assert( std::is_lt(O{} <=> O{1}) ); + static_assert( std::is_gt(O{1} <=> O{}) ); + static_assert( std::is_eq(O{1} <=> O{1}) ); + static_assert( std::is_lt(O{1} <=> O{2}) ); + + static_assert( O{} == O{} ); + static_assert( O{} < O{1} ); + static_assert( O{1} > O{} ); + static_assert( O{1} == O{1} ); + static_assert( O{1} != O{2} ); + static_assert( O{1} < O{2} ); + + using Os = std::optional; + static_assert( std::is_eq(O{} <=> Os{}) ); + static_assert( std::is_lt(O{} <=> Os{1}) ); + static_assert( std::is_gt(O{1} <=> Os{}) ); + static_assert( std::is_eq(O{1} <=> Os{1}) ); + static_assert( std::is_lt(O{1} <=> Os{2}) ); + + static_assert( O{} == Os{} ); + static_assert( O{} < Os{1} ); + static_assert( O{1} > Os{} ); + static_assert( O{1} == Os{1} ); + static_assert( O{1} != Os{2} ); + static_assert( O{1} < Os{2} ); + + // Would requires narrowing conversion to make operands the same type: + static_assert( !std::three_way_comparable_with> ); +} + +void +test02() +{ + using O = std::optional; + using std::nullopt; + + static_assert( std::is_eq(O{} <=> nullopt) ); + static_assert( std::is_gt(O{1} <=> nullopt) ); + static_assert( std::is_lt(nullopt <=> O{1}) ); + + static_assert( O{} == nullopt ); + static_assert( O{1} != nullopt ); + static_assert( nullopt != O{1} ); + static_assert( O{1} > nullopt ); + static_assert( nullopt < O{1} ); + static_assert( nullopt <= O{} ); + static_assert( nullopt <= O{1} ); +} diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/three_way.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/three_way.cc new file mode 100644 index 00000000000..f8752d71c06 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/three_way.cc @@ -0,0 +1,102 @@ +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// Tuple + +#include +#include + +using namespace std; + +template +bool self_consistent(const T& x) +{ + return std::is_eq(x <=> x) && x == x && !(x != x) && x <= x && !(x < x); +} + +void +test01() +{ + int i=0; + int j=0; + int k=2; + tuple a(0, 0, 0); + tuple b(0, 0, 1); + tuple c(i,j,k); + tuple d(c); + VERIFY( self_consistent(a) ); + VERIFY( self_consistent(b) ); + VERIFY( self_consistent(c) ); + VERIFY( self_consistent(d) ); + VERIFY( !(a > a) && !(b > b) ); + VERIFY( a >= a && b >= b ); + VERIFY( a < b && !(b < a) && a <= b && !(b <= a) ); + VERIFY( b > a && !(a > b) && b >= a && !(a >= b) ); + + VERIFY( std::is_lt(a <=> b) ); + VERIFY( std::is_gt(b <=> a) ); + VERIFY( std::is_gt(c <=> a) ); + VERIFY( std::is_eq(c <=> d) ); + + static_assert( std::is_same_v d), std::strong_ordering> ); +} + +template +constexpr bool +check_compare(T&& t, U&& u, C c) +{ + using R = std::compare_three_way_result_t; + static_assert( std::same_as ); + return (t <=> u) == c; +} + +void +test02() +{ + using std::strong_ordering; + using std::weak_ordering; + using std::partial_ordering; + + using T0 = std::tuple<>; + static_assert( check_compare(T0(), T0(), strong_ordering::equal) ); + + using Ti = std::tuple; + using Tu = std::tuple; + static_assert( check_compare(Ti(1), Tu(1u), weak_ordering::equivalent) ); + static_assert( check_compare(Ti(1), Tu(2u), weak_ordering::less) ); + static_assert( check_compare(Ti(-1), Tu(1u), weak_ordering::greater) ); + + using Tii = std::tuple; + using Tlu = std::tuple; + static_assert( check_compare(Tii(1, 2), Tlu(2l, 1u), weak_ordering::less) ); + + using Tid = std::tuple; + static_assert( check_compare(Tii(3, 4), Tid(2, 0.9), partial_ordering::greater) ); + + static_assert( !std::three_way_comparable_with ); + static_assert( !std::three_way_comparable_with ); + static_assert( !std::three_way_comparable_with ); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/20_util/variant/89851.cc b/libstdc++-v3/testsuite/20_util/variant/relops/89851.cc similarity index 100% rename from libstdc++-v3/testsuite/20_util/variant/89851.cc rename to libstdc++-v3/testsuite/20_util/variant/relops/89851.cc diff --git a/libstdc++-v3/testsuite/20_util/variant/90008.cc b/libstdc++-v3/testsuite/20_util/variant/relops/90008.cc similarity index 100% rename from libstdc++-v3/testsuite/20_util/variant/90008.cc rename to libstdc++-v3/testsuite/20_util/variant/relops/90008.cc diff --git a/libstdc++-v3/testsuite/20_util/variant/relops/three_way.cc b/libstdc++-v3/testsuite/20_util/variant/relops/three_way.cc new file mode 100644 index 00000000000..b4f78e87d23 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/variant/relops/three_way.cc @@ -0,0 +1,54 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +void +test01() +{ + using V = std::variant; + constexpr auto I0 = std::in_place_index<0>; + constexpr auto I1 = std::in_place_index<1>; + + static_assert( std::is_eq(V{I0, 0} <=> V{I0, 0}) ); + static_assert( std::is_eq(V{I0, 1} <=> V{I0, 1}) ); + + static_assert( std::is_lt(V{I0, 0} <=> V{I1, 0}) ); + static_assert( std::is_lt(V{I0, 1} <=> V{I1, 0}) ); + + static_assert( std::is_gt(V{I0, 1} <=> V{I0, 0}) ); + static_assert( std::is_gt(V{I1, 0} <=> V{I0, 1}) ); + + static_assert( V{I0, 0} == V{I0, 0} ); + static_assert( V{I0, 0} != V{I1, 0} ); + static_assert( V{I1, 0} != V{I1, 1} ); +} + +void +test02() +{ + static_assert( std::is_eq(std::monostate{} <=> std::monostate{}) ); + static_assert( std::monostate{} == std::monostate{} ); + static_assert( std::monostate{} <= std::monostate{} ); + static_assert( std::monostate{} >= std::monostate{} ); + static_assert( !(std::monostate{} != std::monostate{}) ); + static_assert( !(std::monostate{} < std::monostate{}) ); + static_assert( !(std::monostate{} > std::monostate{}) ); +} -- 2.30.2