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

Message ID 20191205004132.GA1453513@redhat.com
State New
Headers show
Series
  • libstdc++: Implement spaceship for std::array (P1614R2)
Related show

Commit Message

Jonathan Wakely Dec. 5, 2019, 12:41 a.m.
As done for std::pair, this defines operator<=> as a non-member function
template and does not alter operator==, as expected to be proposed as
the resolution to an unpublished LWG issue.

Instead of calling std::lexicographical_compare_three_way the <=>
overload is implemented by hand to take advantage of the fact the
element types and array sizes are known to be the same.

	* include/bits/cpp_type_traits.h (__is_byte<char8_t>): Add
	specialization.
	* include/std/array (operator<=>): Likewise.
	* testsuite/23_containers/array/comparison_operators/constexpr.cc:
	Test three-way comparisons and arrays of unsigned char.
	* testsuite/23_containers/array/tuple_interface/get_neg.cc: Adjust
	dg-error line numbers.

Tested powerpc64le-linux, committed to trunk.
commit 48d500f7ffbc8f33db4c7b0a6d92f9ad52ef26bb
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Dec 4 17:20:33 2019 +0000

    libstdc++: Implement spaceship for std::array (P1614R2)
    
    As done for std::pair, this defines operator<=> as a non-member function
    template and does not alter operator==, as expected to be proposed as
    the resolution to an unpublished LWG issue.
    
    Instead of calling std::lexicographical_compare_three_way the <=>
    overload is implemented by hand to take advantage of the fact the
    element types and array sizes are known to be the same.
    
            * include/bits/cpp_type_traits.h (__is_byte<char8_t>): Add
            specialization.
            * include/std/array (operator<=>): Likewise.
            * testsuite/23_containers/array/comparison_operators/constexpr.cc:
            Test three-way comparisons and arrays of unsigned char.
            * testsuite/23_containers/array/tuple_interface/get_neg.cc: Adjust
            dg-error line numbers.

Patch

diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
index 3e165c77707..28180bfc06d 100644
--- a/libstdc++-v3/include/bits/cpp_type_traits.h
+++ b/libstdc++-v3/include/bits/cpp_type_traits.h
@@ -411,6 +411,15 @@  __INT_N(__GLIBCXX_TYPE_INT_N_3)
     };
 #endif // C++17
 
+#ifdef _GLIBCXX_USE_CHAR8_T
+  template<>
+    struct __is_byte<char8_t>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+#endif
+
   //
   // Move iterator type
   //
diff --git a/libstdc++-v3/include/std/array b/libstdc++-v3/include/std/array
index 9ad1e652b6c..ad3f6518da2 100644
--- a/libstdc++-v3/include/std/array
+++ b/libstdc++-v3/include/std/array
@@ -253,6 +253,25 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     operator==(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two)
     { return std::equal(__one.begin(), __one.end(), __two.begin()); }
 
+#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
+  template<typename _Tp, size_t _Nm>
+    constexpr __detail::__synth3way_t<_Tp>
+    operator<=>(const array<_Tp, _Nm>& __a, const array<_Tp, _Nm>& __b)
+    {
+      if constexpr (_Nm && __is_byte<_Tp>::__value)
+	return __builtin_memcmp(__a.data(), __b.data(), _Nm) <=> 0;
+      else
+	{
+	  for (size_t __i = 0; __i < _Nm; ++__i)
+	    {
+	      auto __c = __detail::__synth3way(__a[__i], __b[__i]);
+	      if (__c != 0)
+		return __c;
+	    }
+	}
+      return strong_ordering::equal;
+    }
+#else
   template<typename _Tp, std::size_t _Nm>
     _GLIBCXX20_CONSTEXPR
     inline bool
@@ -285,6 +304,7 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     inline bool
     operator>=(const array<_Tp, _Nm>& __one, const array<_Tp, _Nm>& __two)
     { return !(__one < __two); }
+#endif // three_way_comparison && concepts
 
   // Specialized algorithms.
   template<typename _Tp, std::size_t _Nm>
diff --git a/libstdc++-v3/testsuite/23_containers/array/comparison_operators/constexpr.cc b/libstdc++-v3/testsuite/23_containers/array/comparison_operators/constexpr.cc
index 8655a9bede2..91a8a24b9e5 100644
--- a/libstdc++-v3/testsuite/23_containers/array/comparison_operators/constexpr.cc
+++ b/libstdc++-v3/testsuite/23_containers/array/comparison_operators/constexpr.cc
@@ -31,3 +31,23 @@  static_assert(a1 < a3);
 static_assert(a4 > a1);
 static_assert(a1 <= a3);
 static_assert(a4 >= a1);
+static_assert(std::is_eq(a1 <=> a1));
+static_assert(std::is_neq(a1 <=> a2));
+static_assert(std::is_lt(a1 <=> a3));
+static_assert(std::is_gt(a4 <=> a1));
+
+constexpr std::array<unsigned char, 3> a5{{1, 2, 3}};
+constexpr std::array<unsigned char, 3> a6{{4, 5, 6}};
+constexpr std::array<unsigned char, 3> a7{{1, 2, 4}};
+constexpr std::array<unsigned char, 3> a8{{1, 3, 3}};
+
+static_assert(a5 == a5);
+static_assert(a5 != a6);
+static_assert(a5 < a7);
+static_assert(a8 > a5);
+static_assert(a5 <= a7);
+static_assert(a8 >= a5);
+static_assert(std::is_eq(a5 <=> a5));
+static_assert(std::is_neq(a5 <=> a6));
+static_assert(std::is_lt(a5 <=> a7));
+static_assert(std::is_gt(a8 <=> a5));
diff --git a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_neg.cc b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_neg.cc
index 2ae8a5edc67..7833748392a 100644
--- a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_neg.cc
@@ -27,6 +27,6 @@  int n1 = std::get<1>(a);
 int n2 = std::get<1>(std::move(a));
 int n3 = std::get<1>(ca);
 
-// { dg-error "static assertion failed" "" { target *-*-* } 316 }
-// { dg-error "static assertion failed" "" { target *-*-* } 325 }
-// { dg-error "static assertion failed" "" { target *-*-* } 333 }
+// { dg-error "static assertion failed" "" { target *-*-* } 336 }
+// { dg-error "static assertion failed" "" { target *-*-* } 345 }
+// { dg-error "static assertion failed" "" { target *-*-* } 353 }