libstdc++: Make incrementable<__int128> satisfied in strict mode
authorJonathan Wakely <jwakely@redhat.com>
Thu, 20 Aug 2020 18:41:15 +0000 (19:41 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Thu, 20 Aug 2020 18:42:02 +0000 (19:42 +0100)
This adds specializations of std::incrementable_traits so that 128-bit
integers are always considered incrementable (and therefore usable with
std::ranges::iota_view) even when they don't satisfy std::integral.

libstdc++-v3/ChangeLog:

* include/bits/iterator_concepts.h [__STRICT_ANSI__]
(incrementable_traits<__int128>): Define specialization.
(incrementable_traits<unsigned __int128>): Likewise.
* testsuite/std/ranges/iota/96042.cc: Test iota_view with
__int128.

libstdc++-v3/include/bits/iterator_concepts.h
libstdc++-v3/testsuite/std/ranges/iota/96042.cc

index 5033f2bddc396fbb7c0a4417ccec7b2c2a9173ce..bd6660c5f22262ac1704ced98a229cd5023176e8 100644 (file)
@@ -173,6 +173,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        = make_signed_t<decltype(std::declval<_Tp>() - std::declval<_Tp>())>;
     };
 
+#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
+  // __int128 is incrementable even if !integral<__int128>
+  template<>
+    struct incrementable_traits<__int128>
+    { using difference_type = __int128; };
+
+  template<>
+    struct incrementable_traits<unsigned __int128>
+    { using difference_type = __int128; };
+#endif
+
   namespace __detail
   {
     // An iterator such that iterator_traits<_Iter> names a specialization
index 6f5c8f61fd22fc8d1daafb171409dabca159b3f6..911663bc41396d1c42b3cf4f29176ccdff3c3bb0 100644 (file)
@@ -24,8 +24,33 @@ void
 test01()
 {
   // PR libstdc++/96042
-  using V = std::ranges::iota_view<long long, int>;
+  using V = std::ranges::iota_view<long long, long long>;
+
+  // In strict -std=c++20 mode there is no integer wider than long long,
+  // so V's difference type is an integer-class type, [iterator.concept.winc].
+  // In practice this is either __int128 or __detail::__max_diff_type.
   using D = std::ranges::range_difference_t<V>;
+  // Ensure that numeric_limits is correctly specialized for the type.
+  using L = std::numeric_limits<D>;
+  static_assert( L::is_specialized );
+  static_assert( L::is_signed );
+  static_assert( L::is_integer );
+  static_assert( L::is_exact );
+  static_assert( L::digits > std::numeric_limits<long long>::digits );
+  static_assert( L::digits10 == static_cast<int>(L::digits * 0.30103) );
+  static_assert( L::min() == (D(1) << L::digits) );
+  static_assert( L::max() == ~L::min() );
+  static_assert( L::lowest() == L::min() );
+}
+
+#ifdef __SIZEOF_INT128__
+void
+test02()
+{
+  // When the target supports __int128 it can be used in iota_view
+  // even in strict mode where !integral<__int128>.
+  using V = std::ranges::iota_view<__int128, __int128>;
+  using D = std::ranges::range_difference_t<V>; // __detail::__max_diff_type
   using L = std::numeric_limits<D>;
   static_assert( L::is_specialized );
   static_assert( L::is_signed );
@@ -37,3 +62,4 @@ test01()
   static_assert( L::max() == ~L::min() );
   static_assert( L::lowest() == L::min() );
 }
+#endif