libstdc++: Implement spaceship for std::pair (P1614R2)

Message ID 20191203235720.GA1339267@redhat.com
State New
Headers show
Series
  • libstdc++: Implement spaceship for std::pair (P1614R2)
Related show

Commit Message

Jonathan Wakely Dec. 3, 2019, 11:57 p.m.
This defines operator<=> as a non-member function template and does not
alter operator==. This contradicts the changes made by P1614R2, which
specify both as hidden friends, but that specification of operator<=> is
broken and the subject of a soon-to-be-published LWG issue.

	* include/bits/stl_pair.h [__cpp_lib_three_way_comparison]
	(operator<=>): Define for C++20.
	* libsupc++/compare (__cmp2way_res_t): Rename to __cmp3way_res_t,
	move into __detail namespace. Do not turn argument types into lvalues.
	(__cmp3way_helper): Rename to __cmp3way_res_impl, move into __detail
	namespace. Constrain with concepts instead of using void_t.
	(compare_three_way_result): Adjust name of base class.
	(compare_three_way_result_t): Use __cmp3way_res_impl directly.
	(__detail::__3way_cmp_with): Add workaround for PR 91073.
	(compare_three_way): Use workaround.
	(__detail::__synth3way, __detail::__synth3way_t): Define new helpers
	implementing synth-three-way and synth-three-way-result semantics.
	* testsuite/20_util/pair/comparison_operators/constexpr_c++20.cc: New
	test.

Tested powerpc64le-linux, committed to trunk.

I plan to do more of P1614R2 tomorrow, starting with std::array (which
is also broken in the working draft).
commit b763e0d20380f4ef6b6cd41362128e6b78fe5b26
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Dec 3 14:57:00 2019 +0000

    libstdc++: Implement spaceship for std::pair (P1614R2)
    
    This defines operator<=> as a non-member function template and does not
    alter operator==. This contradicts the changes made by P1614R2, which
    specify both as hidden friends, but that specification of operator<=> is
    broken and the subject of a soon-to-be-published LWG issue.
    
            * include/bits/stl_pair.h [__cpp_lib_three_way_comparison]
            (operator<=>): Define for C++20.
            * libsupc++/compare (__cmp2way_res_t): Rename to __cmp3way_res_t,
            move into __detail namespace. Do not turn argument types into lvalues.
            (__cmp3way_helper): Rename to __cmp3way_res_impl, move into __detail
            namespace. Constrain with concepts instead of using void_t.
            (compare_three_way_result): Adjust name of base class.
            (compare_three_way_result_t): Use __cmp3way_res_impl directly.
            (__detail::__3way_cmp_with): Add workaround for PR 91073.
            (compare_three_way): Use workaround.
            (__detail::__synth3way, __detail::__synth3way_t): Define new helpers
            implementing synth-three-way and synth-three-way-result semantics.
            * testsuite/20_util/pair/comparison_operators/constexpr_c++20.cc: New
            test.

Patch

diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 1e0e4fe892a..02b1f0ac922 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -59,7 +59,10 @@ 
 #include <bits/move.h> // for std::move / std::forward, and std::swap
 
 #if __cplusplus >= 201103L
-#include <type_traits> // for std::__decay_and_strip too
+# include <type_traits> // for std::__decay_and_strip, std::is_reference_v
+#endif
+#if __cplusplus > 201703L
+# include <compare>
 #endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
@@ -447,7 +450,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_GLIBCXX20_CONSTEXPR
         pair(tuple<_Args1...>&, tuple<_Args2...>&,
              _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
-#endif
+#endif // C++11
     };
 
   /// @relates pair @{
@@ -462,6 +465,17 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
     { return __x.first == __y.first && __x.second == __y.second; }
 
+#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
+  template<typename _T1, typename _T2>
+    constexpr common_comparison_category_t<__detail::__synth3way_t<_T1>,
+					   __detail::__synth3way_t<_T2>>
+    operator<=>(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+    {
+      if (auto __c = __detail::__synth3way(__x.first, __y.first); __c != 0)
+	return __c;
+      return __detail::__synth3way(__x.second, __y.second);
+    }
+#else
   /** Defines a lexicographical order for pairs.
    *
    * For two pairs of the same type, `P` is ordered before `Q` if
@@ -498,6 +512,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline _GLIBCXX_CONSTEXPR bool
     operator>=(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
     { return !(__x < __y); }
+#endif // !(three_way_comparison && concepts)
 
 #if __cplusplus >= 201103L
   /** Swap overload for pairs. Calls std::pair::swap().
diff --git a/libstdc++-v3/libsupc++/compare b/libstdc++-v3/libsupc++/compare
index 289145dea56..412ec6861d3 100644
--- a/libstdc++-v3/libsupc++/compare
+++ b/libstdc++-v3/libsupc++/compare
@@ -509,34 +509,41 @@  namespace std
 	{ __t <=> __u } -> __detail::__compares_as<_Cat>;
 	{ __u <=> __t } -> __detail::__compares_as<_Cat>;
       };
-#endif
 
-  template<typename _Tp, typename _Up>
-    using __cmp2way_res_t
-      = decltype(std::declval<_Tp&>() <=> std::declval<_Up&>());
+  namespace __detail
+  {
+    template<typename _Tp, typename _Up>
+      using __cmp3way_res_t
+	= decltype(std::declval<_Tp>() <=> std::declval<_Up>());
 
-  template<typename _Tp, typename _Up = _Tp, typename = void>
-    struct __cmp3way_helper
-    { };
+    // Implementation of std::compare_three_way_result.
+    // It is undefined for a program to add specializations of
+    // std::compare_three_way_result, so the std::compare_three_way_result_t
+    // alias ignores std::compare_three_way_result and uses
+    // __detail::__cmp3way_res_impl directly instead.
+    template<typename _Tp, typename _Up>
+      struct __cmp3way_res_impl
+      { };
 
-  template<typename _Tp, typename _Up>
-    struct __cmp3way_helper<_Tp, _Up, void_t<__cmp2way_res_t<_Tp, _Up>>>
-    {
-      using type = __cmp2way_res_t<_Tp, _Up>;
-      using __type = type;
-    };
+    template<typename _Tp, typename _Up>
+      requires requires { typename __cmp3way_res_t<__cref<_Tp>, __cref<_Up>>; }
+      struct __cmp3way_res_impl<_Tp, _Up>
+      {
+	using type = __cmp3way_res_t<__cref<_Tp>, __cref<_Up>>;
+      };
+  } // namespace __detail
 
   /// [cmp.result], result of three-way comparison
   template<typename _Tp, typename _Up = _Tp>
     struct compare_three_way_result
-    : __cmp3way_helper<_Tp, _Up>
+    : __detail::__cmp3way_res_impl<_Tp, _Up>
     { };
 
+  /// [cmp.result], result of three-way comparison
   template<typename _Tp, typename _Up = _Tp>
     using compare_three_way_result_t
-      = typename compare_three_way_result<_Tp, _Up>::__type;
+      = typename __detail::__cmp3way_res_impl<_Tp, _Up>::type;
 
-#if __cpp_lib_concepts
   namespace __detail
   {
     // BUILTIN-PTR-THREE-WAY(T, U)
@@ -548,13 +555,17 @@  namespace std
 	     { operator<=>(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u)); }
 	  && ! requires(_Tp&& __t, _Up&& __u)
 	     { static_cast<_Tp&&>(__t).operator<=>(static_cast<_Up&&>(__u)); };
+
+    // FIXME: workaround for PR c++/91073
+    template<typename _Tp, typename _Up>
+      concept __3way_cmp_with = three_way_comparable_with<_Tp, _Up>;
   } // namespace __detail
 
   // [cmp.object], typename compare_three_way
   struct compare_three_way
   {
     template<typename _Tp, typename _Up>
-      requires (three_way_comparable_with<_Tp, _Up>
+      requires (__detail::__3way_cmp_with<_Tp, _Up>
 	  || __detail::__3way_builtin_ptr_cmp<_Tp, _Up>)
       constexpr auto
       operator()(_Tp&& __t, _Up&& __u) const noexcept
@@ -915,6 +926,40 @@  namespace std
     inline constexpr __cmp_cust::_Partial_fallback
     compare_partial_order_fallback{};
   }
+
+  namespace __detail
+  {
+    // [expos.only.func]
+    inline constexpr struct _Synth3way
+    {
+      template<typename _Tp, typename _Up>
+	constexpr auto
+	operator()(const _Tp& __t, const _Up& __u) const
+	requires requires
+	{
+	  { __t < __u } -> convertible_to<bool>;
+	  { __u < __t } -> convertible_to<bool>;
+	}
+	{
+	  if constexpr (__3way_cmp_with<_Tp, _Up>)
+	    return __t <=> __u;
+	  else
+	    {
+	      if (__t < __u)
+		return weak_ordering::less;
+	      else if (__u < __t)
+		return weak_ordering::greater;
+	      else
+		return weak_ordering::equivalent;
+	    }
+	}
+    } __synth3way = {};
+
+    template<typename _Tp, typename _Up = _Tp>
+      using __synth3way_t
+	= decltype(__detail::__synth3way(std::declval<_Tp&>(),
+					 std::declval<_Up&>()));
+  } // namespace __detail
 #endif // concepts
 } // namespace std
 
diff --git a/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constexpr_c++20.cc b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constexpr_c++20.cc
new file mode 100644
index 00000000000..127610d8368
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constexpr_c++20.cc
@@ -0,0 +1,31 @@ 
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+// Copyright (C) 2019 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
+// <http://www.gnu.org/licenses/>.
+
+#include <utility>
+#include <testsuite_common_types.h>
+
+int main()
+{
+  __gnu_test::constexpr_comparison_operators test;
+  test.operator()<std::pair<int, int>>();
+
+  constexpr std::pair<int, int> p{1, 2}, q{3, 0};
+  static_assert( p <=> q == std::strong_ordering::less );
+}