@@ -482,6 +482,66 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
: __is_nonvolatile_trivially_copyable<_Tp>
{ };
+ // Whether memcmp can be used to determine ordering for a type
+ // e.g. in std::lexicographical_compare or three-way comparisons.
+ // True for unsigned integer-like types where comparing each byte in turn
+ // as an unsigned char yields the right result. This is true for all
+ // unsigned integers on big endian targets, but only unsigned narrow
+ // character types (and std::byte) on little endian targets.
+ template<typename _Tp, bool _TreatAsBytes =
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ __is_integer<_Tp>::__value
+#else
+ __is_byte<_Tp>::__value
+#endif
+ >
+ struct __is_memcmp_ordered
+ {
+ static const bool __value = _Tp(-1) > _Tp(1); // is unsigned
+ };
+
+ template<typename _Tp>
+ struct __is_memcmp_ordered<_Tp, false>
+ {
+ static const bool __value = false;
+ };
+
+ // Whether two types can be compared using memcmp.
+ template<typename _Tp, typename _Up, bool = sizeof(_Tp) == sizeof(_Up)>
+ struct __is_memcmp_ordered_with
+ {
+ static const bool __value = __is_memcmp_ordered<_Tp>::__value
+ && __is_memcmp_ordered<_Up>::__value;
+ };
+
+ template<typename _Tp, typename _Up>
+ struct __is_memcmp_ordered_with<_Tp, _Up, false>
+ {
+ static const bool __value = false;
+ };
+
+#if __cplusplus >= 201703L
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ // std::byte is not an integer, but it can be compared using memcmp.
+ template<>
+ struct __is_memcmp_ordered<std::byte, false>
+ { static constexpr bool __value = true; };
+#endif
+
+ // std::byte can only be compared to itself, not to other types.
+ template<>
+ struct __is_memcmp_ordered_with<std::byte, std::byte, true>
+ { static constexpr bool __value = true; };
+
+ template<typename _Tp, bool _SameSize>
+ struct __is_memcmp_ordered_with<_Tp, std::byte, _SameSize>
+ { static constexpr bool __value = false; };
+
+ template<typename _Up, bool _SameSize>
+ struct __is_memcmp_ordered_with<std::byte, _Up, _SameSize>
+ { static constexpr bool __value = false; };
+#endif
+
//
// Move iterator type
//
@@ -1269,9 +1269,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
const _Tp2* __first2, const _Tp2* __last2)
{
const bool __simple =
- (__is_byte<_Tp1>::__value && __is_byte<_Tp2>::__value
- && !__gnu_cxx::__numeric_traits<_Tp1>::__is_signed
- && !__gnu_cxx::__numeric_traits<_Tp2>::__is_signed
+ (__is_memcmp_ordered_with<_Tp1, _Tp2>::__value
&& __is_pointer<_Ptr>::__value
#if __cplusplus > 201703L && __cpp_lib_concepts
// For C++20 iterator_traits<volatile T*>::value_type is non-volatile
@@ -1327,9 +1325,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
_GLIBCXX_STD_C::_Deque_iterator<_Tp2, _Ref2, _Ptr2> __last2)
{
const bool __simple =
- (__is_byte<_Tp1>::__value && __is_byte<_Tp2>::__value
- && !__gnu_cxx::__numeric_traits<_Tp1>::__is_signed
- && !__gnu_cxx::__numeric_traits<_Tp2>::__is_signed
+ (__is_memcmp_ordered_with<_Tp1, _Tp2>::__value
&& __is_pointer<_Ptr1>::__value
&& __is_pointer<_Ptr2>::__value
#if __cplusplus > 201703L && __cpp_lib_concepts
@@ -3475,10 +3475,7 @@ namespace ranges
// This condition is consistent with the one in
// __lexicographical_compare_aux in <bits/stl_algobase.h>.
constexpr bool __use_memcmp
- = (__is_byte<_ValueType1>::__value
- && __is_byte<_ValueType2>::__value
- && !__gnu_cxx::__numeric_traits<_ValueType1>::__is_signed
- && !__gnu_cxx::__numeric_traits<_ValueType2>::__is_signed
+ = (__is_memcmp_ordered_with<_ValueType1, _ValueType2>::__value
&& __ptr_to_nonvolatile<_Iter1>
&& __ptr_to_nonvolatile<_Iter2>
&& (is_same_v<_Comp, ranges::less>
@@ -1368,9 +1368,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
typedef typename iterator_traits<_II1>::value_type _ValueType1;
typedef typename iterator_traits<_II2>::value_type _ValueType2;
const bool __simple =
- (__is_byte<_ValueType1>::__value && __is_byte<_ValueType2>::__value
- && !__gnu_cxx::__numeric_traits<_ValueType1>::__is_signed
- && !__gnu_cxx::__numeric_traits<_ValueType2>::__is_signed
+ (__is_memcmp_ordered_with<_ValueType1, _ValueType2>::__value
&& __is_pointer<_II1>::__value
&& __is_pointer<_II2>::__value
#if __cplusplus > 201703L && __cpp_lib_concepts
@@ -1785,8 +1783,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
// or std::byte, suitable for comparison by memcmp.
template<typename _Iter>
concept __is_byte_iter = contiguous_iterator<_Iter>
- && __is_byte<iter_value_t<_Iter>>::__value != 0
- && !__gnu_cxx::__numeric_traits<iter_value_t<_Iter>>::__is_signed;
+ && __is_memcmp_ordered<iter_value_t<_Iter>>::__value;
// Return a struct with two members, initialized to the smaller of x and y
// (or x if they compare equal) and the result of the comparison x <=> y.
@@ -258,16 +258,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
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
+#ifdef __cpp_lib_is_constant_evaluated
+ if constexpr (_Nm && __is_memcmp_ordered<_Tp>::__value)
+ if (!std::is_constant_evaluated())
+ {
+ constexpr size_t __n = _Nm * sizeof(_Tp);
+ return __builtin_memcmp(__a.data(), __b.data(), __n) <=> 0;
+ }
+#endif
+
+ for (size_t __i = 0; __i < _Nm; ++__i)
{
- for (size_t __i = 0; __i < _Nm; ++__i)
- {
- auto __c = __detail::__synth3way(__a[__i], __b[__i]);
- if (__c != 0)
- return __c;
- }
+ auto __c = __detail::__synth3way(__a[__i], __b[__i]);
+ if (__c != 0)
+ return __c;
}
return strong_ordering::equal;
}
new file mode 100644
@@ -0,0 +1,119 @@
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <array>
+#include <testsuite_hooks.h>
+
+template<typename T>
+constexpr auto
+cmp_array(T t)
+{
+ std::array<T, 2> a{ T{1}, t };
+ std::array<T, 2> b{ T{1}, T{2} };
+ return a <=> b;
+}
+
+void
+test01()
+{
+ static_assert( cmp_array((unsigned char)1) < 0 );
+ static_assert( cmp_array((unsigned char)2) == 0 );
+ static_assert( cmp_array((unsigned char)3) > 0 );
+
+ static_assert( cmp_array((signed char)-1) < 0 );
+ static_assert( cmp_array((signed char)2) == 0 );
+ static_assert( cmp_array((signed char)3) > 0 );
+
+ static_assert( cmp_array(std::byte{1}) < 0 );
+ static_assert( cmp_array(std::byte{2}) == 0 );
+ static_assert( cmp_array(std::byte{3}) > 0 );
+
+ static_assert( cmp_array((unsigned int)1) < 0 );
+ static_assert( cmp_array((unsigned int)2) == 0 );
+ static_assert( cmp_array((unsigned int)333) > 0 );
+ static_assert( cmp_array((unsigned int)4444) > 0 );
+ static_assert( cmp_array((unsigned int)55555) > 0 );
+ static_assert( cmp_array((unsigned int)0x66666666) > 0 );
+
+ static_assert( cmp_array((signed int)-1) < 0 );
+ static_assert( cmp_array((signed int)2) == 0 );
+ static_assert( cmp_array((signed int)333) > 0 );
+ static_assert( cmp_array((signed int)-4444) < 0 );
+ static_assert( cmp_array((signed int)55555) > 0 );
+ static_assert( cmp_array((signed int)-0x66666666) < 0 );
+}
+
+void
+test02()
+{
+ unsigned char uc = 1;
+ VERIFY( cmp_array(uc) < 0 );
+ uc = 2;
+ VERIFY( cmp_array(uc) == 0 );
+ uc = 3;
+ VERIFY( cmp_array(uc) > 0 );
+
+ signed char sc = -1;
+ VERIFY( cmp_array(sc) < 0 );
+ sc = 2;
+ VERIFY( cmp_array(sc) == 0 );
+ sc = 3;
+ VERIFY( cmp_array(sc) > 0 );
+
+ std::byte b{1};
+ VERIFY( cmp_array(b) < 0 );
+ b = std::byte{2};
+ VERIFY( cmp_array(b) == 0 );
+ b = std::byte{3};
+ VERIFY( cmp_array(b) > 0 );
+
+ unsigned int ui = 1;
+ VERIFY( cmp_array(ui) < 0 );
+ ui = 2;
+ VERIFY( cmp_array(ui) == 0 );
+ ui = 333;
+ VERIFY( cmp_array(ui) > 0 );
+ ui = 4444;
+ VERIFY( cmp_array(ui) > 0 );
+ ui = 555555;
+ VERIFY( cmp_array(ui) > 0 );
+ ui = 0x66666666;
+ VERIFY( cmp_array(ui) > 0 );
+
+ signed int si = -1;
+ VERIFY( cmp_array(si) < 0 );
+ si = 2;
+ VERIFY( cmp_array(si) == 0 );
+ si = 333;
+ VERIFY( cmp_array(si) > 0 );
+ si = -4444;
+ VERIFY( cmp_array(si) < 0 );
+ si = 555555;
+ VERIFY( cmp_array(si) > 0 );
+ si = -0x66666666;
+ VERIFY( cmp_array(si) < 0 );
+}
+
+int
+main()
+{
+ test01();
+ test02();
+}
@@ -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 *-*-* } 336 }
-// { dg-error "static assertion failed" "" { target *-*-* } 345 }
-// { dg-error "static assertion failed" "" { target *-*-* } 353 }
+// { dg-error "static assertion failed" "" { target *-*-* } 340 }
+// { dg-error "static assertion failed" "" { target *-*-* } 349 }
+// { dg-error "static assertion failed" "" { target *-*-* } 357 }