]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Treat __int128 as a real integral type [PR96710]
authorJonathan Wakely <jwakely@redhat.com>
Fri, 16 May 2025 12:33:23 +0000 (13:33 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Fri, 11 Jul 2025 07:34:13 +0000 (08:34 +0100)
Since LWG 3828 (included in C++23) implementations are allowed to have
extended integer types that are wider than intmax_t. This means we no
longer have to make is_integral_v<__int128> false for strict -std=c++23
mode, removing the confusing inconsistency with -std=gnu++23 (where
is_integral_v<__int128> is true).

This change makes __int128 a true integral type for all modes, treating
LWG 3828 as a DR for previous standards. Most of the change just
involves removing special cases where we wanted to treat __int128 and
unsigned __int128 as integral types even when is_integral_v was false.

There are still some preprocessor conditionals needed, because on some
targets the compiler defines the macro __GLIBCXX_TYPE_INT_N_0 as
__int128 in non-strict modes. Because we define explicit specializations
of templates such as is_integral for all the INT_N types, we already
have a specialization of is_integral<__int128> in non-strict modes, and
so to avoid a redefinition we only must only define
is_integral<__int128> for strict modes.

libstdc++-v3/ChangeLog:

PR libstdc++/96710
* include/bits/cpp_type_traits.h (__is_integer): Define explicit
specializations for __int128.
(__memcpyable_integer): Remove explicit specializations for
__int128.
* include/bits/iterator_concepts.h (incrementable_traits):
Likewise.
(__is_signed_int128, __is_unsigned_int128, __is_int128): Remove.
(__is_integer_like, __is_signed_integer_like): Remove check for
__int128.
* include/bits/max_size_type.h: Remove all uses of __is_int128
in constraints.
* include/bits/ranges_base.h (__to_unsigned_like): Remove
overloads for __int128.
(ranges::ssize): Remove special case for __int128.
* include/bits/stl_algobase.h (__size_to_integer): Define
__int128 overloads for strict modes.
* include/ext/numeric_traits.h (__is_integer_nonstrict): Remove
explicit specializations for __int128.
* include/std/charconv (to_chars): Define overloads for
__int128.
* include/std/format (__format::make_unsigned_t): Remove.
(__format::to_chars): Remove.
* include/std/limits (numeric_limits): Define explicit
specializations for __int128.
* include/std/type_traits (__is_integral_helper): Likewise.
(__make_unsigned, __make_signed): Likewise.

Reviewed-by: Patrick Palka <ppalka@redhat.com>
libstdc++-v3/include/bits/cpp_type_traits.h
libstdc++-v3/include/bits/iterator_concepts.h
libstdc++-v3/include/bits/max_size_type.h
libstdc++-v3/include/bits/ranges_base.h
libstdc++-v3/include/bits/stl_algobase.h
libstdc++-v3/include/ext/numeric_traits.h
libstdc++-v3/include/std/charconv
libstdc++-v3/include/std/format
libstdc++-v3/include/std/limits
libstdc++-v3/include/std/type_traits

index b1a6206ce1eb6fe213824d3db1508e6d1d0c2694..770ad94b3b4de6c1dd2fe1a4b2970f03f00ec3ec 100644 (file)
@@ -273,6 +273,12 @@ __INT_N(__GLIBCXX_TYPE_INT_N_2)
 __INT_N(__GLIBCXX_TYPE_INT_N_3)
 #endif
 
+#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
+// In strict modes __GLIBCXX_TYPE_INT_N_0 is not defined for __int128,
+// but we want to always treat signed/unsigned __int128 as integral types.
+__INT_N(__int128)
+#endif
+
 #undef __INT_N
 
   //
@@ -545,17 +551,6 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
     { enum { __width = __GLIBCXX_BITSIZE_INT_N_3 }; };
 #endif
 
-#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
-  // In strict modes __is_integer<__int128> is false,
-  // but we want to allow memcpy between signed/unsigned __int128.
-  __extension__
-  template<>
-    struct __memcpyable_integer<__int128> { enum { __width = 128 }; };
-  __extension__
-  template<>
-    struct __memcpyable_integer<unsigned __int128> { enum { __width = 128 }; };
-#endif
-
 #if _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 && _GLIBCXX_LDOUBLE_IS_IEEE_BINARY64
   template<>
     struct __memcpyable<double*, long double*> { enum { __value = true }; };
index d31e4f1451075d50fad2486c2b0869635e67e520..979039e7da53d91e590b039528dc351073bd702a 100644 (file)
@@ -214,17 +214,6 @@ _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
@@ -611,41 +600,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     class __max_diff_type;
     class __max_size_type;
 
-    __extension__
-    template<typename _Tp>
-      concept __is_signed_int128
-#if __SIZEOF_INT128__
-       = same_as<_Tp, __int128>;
-#else
-       = false;
-#endif
-
-    __extension__
-    template<typename _Tp>
-      concept __is_unsigned_int128
-#if __SIZEOF_INT128__
-       = same_as<_Tp, unsigned __int128>;
-#else
-       = false;
-#endif
-
     template<typename _Tp>
       concept __cv_bool = same_as<const volatile _Tp, const volatile bool>;
 
     template<typename _Tp>
       concept __integral_nonbool = integral<_Tp> && !__cv_bool<_Tp>;
 
-    template<typename _Tp>
-      concept __is_int128 = __is_signed_int128<_Tp> || __is_unsigned_int128<_Tp>;
-
     template<typename _Tp>
       concept __is_integer_like = __integral_nonbool<_Tp>
-       || __is_int128<_Tp>
        || same_as<_Tp, __max_diff_type> || same_as<_Tp, __max_size_type>;
 
     template<typename _Tp>
       concept __is_signed_integer_like = signed_integral<_Tp>
-       || __is_signed_int128<_Tp>
        || same_as<_Tp, __max_diff_type>;
 
   } // namespace ranges::__detail
index 30c5b1247679c2971d708604138d28d020adda39..a34b91a04f1e6b82c78792c41fc1e9b73529c8b1 100644 (file)
@@ -65,7 +65,7 @@ namespace ranges
     public:
       __max_size_type() = default;
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        constexpr
        __max_size_type(_Tp __i) noexcept
          : _M_val(__i), _M_msb(__i < 0)
@@ -74,7 +74,7 @@ namespace ranges
       constexpr explicit
       __max_size_type(const __max_diff_type& __d) noexcept;
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        constexpr explicit
        operator _Tp() const noexcept
        { return _M_val; }
@@ -260,52 +260,52 @@ namespace ranges
        return *this;
       }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator+=(_Tp& __a, const __max_size_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a + __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator-=(_Tp& __a, const __max_size_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a - __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator*=(_Tp& __a, const __max_size_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a * __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator/=(_Tp& __a, const __max_size_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a / __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator%=(_Tp& __a, const __max_size_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a % __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator&=(_Tp& __a, const __max_size_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a & __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator|=(_Tp& __a, const __max_size_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a | __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator^=(_Tp& __a, const __max_size_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a ^ __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator<<=(_Tp& __a, const __max_size_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a << __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator>>=(_Tp& __a, const __max_size_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a >> __b)); }
@@ -447,7 +447,7 @@ namespace ranges
     public:
       __max_diff_type() = default;
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        constexpr
        __max_diff_type(_Tp __i) noexcept
          : _M_rep(__i)
@@ -458,7 +458,7 @@ namespace ranges
        : _M_rep(__d)
       { }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        constexpr explicit
        operator _Tp() const noexcept
        { return static_cast<_Tp>(_M_rep); }
@@ -591,52 +591,52 @@ namespace ranges
        return *this;
       }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator+=(_Tp& __a, const __max_diff_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a + __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator-=(_Tp& __a, const __max_diff_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a - __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator*=(_Tp& __a, const __max_diff_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a * __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator/=(_Tp& __a, const __max_diff_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a / __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator%=(_Tp& __a, const __max_diff_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a % __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator&=(_Tp& __a, const __max_diff_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a & __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator|=(_Tp& __a, const __max_diff_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a | __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator^=(_Tp& __a, const __max_diff_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a ^ __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator<<=(_Tp& __a, const __max_diff_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a << __b)); }
 
-      template<typename _Tp> requires integral<_Tp> || __is_int128<_Tp>
+      template<typename _Tp> requires integral<_Tp>
        friend constexpr _Tp&
        operator>>=(_Tp& __a, const __max_diff_type& __b) noexcept
        { return (__a = static_cast<_Tp>(__a >> __b)); }
index c09f7292067d39291978c358cf45ff39227c2462..0251e5d0928a99de86a9c2654bb8bfdfe5a1a757 100644 (file)
@@ -81,16 +81,6 @@ namespace ranges
       __to_unsigned_like(_Tp __t) noexcept
       { return static_cast<make_unsigned_t<_Tp>>(__t); }
 
-#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
-    constexpr unsigned __int128
-    __to_unsigned_like(__int128 __t) noexcept
-    { return __t; }
-
-    constexpr unsigned __int128
-    __to_unsigned_like(unsigned __int128 __t) noexcept
-    { return __t; }
-#endif
-
     template<typename _Tp>
       using __make_unsigned_like_t
        = decltype(__detail::__to_unsigned_like(std::declval<_Tp>()));
@@ -398,11 +388,6 @@ namespace ranges
              else
                return static_cast<make_signed_t<__size_type>>(__size);
            }
-#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
-         // For strict-ansi modes integral<__int128> is false
-         else if constexpr (__detail::__is_int128<__size_type>)
-           return static_cast<__int128>(__size);
-#endif
          else // Must be one of __max_diff_type or __max_size_type.
            return __detail::__max_diff_type(__size);
        }
index 4d5662ca45bf51157f0a995d6b7b5f420565d6c9..71ef2335a311f0f67363136d930a4cc1327e8f60 100644 (file)
@@ -1052,6 +1052,13 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
   __size_to_integer(unsigned __GLIBCXX_TYPE_INT_N_3 __n) { return __n; }
 #endif
 
+#if defined(__STRICT_ANSI__) && defined(__SIZEOF_INT128__)
+  __extension__ inline _GLIBCXX_CONSTEXPR __int128
+  __size_to_integer(__int128 __n) { return __n; }
+  __extension__ inline _GLIBCXX_CONSTEXPR unsigned __int128
+  __size_to_integer(unsigned __int128 __n) { return __n; }
+#endif
+
   inline _GLIBCXX_CONSTEXPR long long
   __size_to_integer(float __n) { return (long long)__n; }
   inline _GLIBCXX_CONSTEXPR long long
index 2cd89430f51c2c3dee40b339c44dc8100750a51d..6786dc6a71bc2cc0f938b358d9fc0bf014e9c417 100644 (file)
@@ -126,12 +126,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   _GLIBCXX_INT_N_TRAITS(__GLIBCXX_TYPE_INT_N_3, __GLIBCXX_BITSIZE_INT_N_3)
 #endif
 
-#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
-  // In strict modes __is_integer<__int128> is false,
-  // but we still want to define __numeric_traits_integer<__int128>.
-  _GLIBCXX_INT_N_TRAITS(__int128, 128)
-#endif
-
 #undef _GLIBCXX_INT_N_TRAITS
 
 #if __cplusplus >= 201103L
index dda49ce72d0b53c7a6e86c2e3fb510d0218fd5a6..8cf2c0b01d7c653e61b80d4002d0a05cf0ee621f 100644 (file)
@@ -390,6 +390,10 @@ _GLIBCXX_TO_CHARS(unsigned __GLIBCXX_TYPE_INT_N_2)
 _GLIBCXX_TO_CHARS(signed __GLIBCXX_TYPE_INT_N_3)
 _GLIBCXX_TO_CHARS(unsigned __GLIBCXX_TYPE_INT_N_3)
 #endif
+#if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__
+_GLIBCXX_TO_CHARS(signed __int128)
+_GLIBCXX_TO_CHARS(unsigned __int128)
+#endif
 #undef _GLIBCXX_TO_CHARS
 
   // _GLIBCXX_RESOLVE_LIB_DEFECTS
index d584b81c78a1f99d13421d5b4bcfec7ad33854c6..d63c6fc9efd55799feb3e423dfcf05d2e9bb9d4a 100644 (file)
@@ -1854,20 +1854,6 @@ namespace __format
                                          __align, __nfill, __fill_char);
        }
 
-#if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__
-      template<typename _Tp>
-       using make_unsigned_t
-         = typename __conditional_t<(sizeof(_Tp) <= sizeof(long long)),
-                                    std::make_unsigned<_Tp>,
-                                    type_identity<unsigned __int128>>::type;
-
-      // std::to_chars is not overloaded for int128 in strict mode.
-      template<typename _Int>
-       static to_chars_result
-       to_chars(char* __first, char* __last, _Int __value, int __base)
-       { return std::__to_chars_i<_Int>(__first, __last, __value, __base); }
-#endif
-
       _Spec<_CharT> _M_spec{};
     };
 
index 2331c25599a0ca2ecb8dbec49bbcb3c8e85a417c..3567a32840069a26a489e0d04669d5ab11c36c7d 100644 (file)
@@ -1639,7 +1639,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #define __INT_N_U201103(TYPE)
 #endif
 
-#if !defined(__STRICT_ANSI__)
 #ifdef __GLIBCXX_TYPE_INT_N_0
   __INT_N(__GLIBCXX_TYPE_INT_N_0, __GLIBCXX_BITSIZE_INT_N_0,
          __INT_N_201103 (__GLIBCXX_TYPE_INT_N_0),
@@ -1661,7 +1660,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          __INT_N_U201103 (__GLIBCXX_TYPE_INT_N_3))
 #endif
 
-#elif defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
+#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
   __INT_N(__int128, 128,
          __INT_N_201103 (__int128),
          __INT_N_U201103 (__int128))
index 055411195f17e9c323a4d0603cbc40dcdc0bf8a4..e88d04e44d76fefd391e3807749c6a069ec18387 100644 (file)
@@ -464,6 +464,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_3>
     : public true_type { };
 #endif
+
+#if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__
+  __extension__
+  template<>
+    struct __is_integral_helper<__int128>
+    : public true_type { };
+
+  __extension__
+  template<>
+    struct __is_integral_helper<unsigned __int128>
+    : public true_type { };
+#endif
+
   /// @endcond
 
   /// is_integral
@@ -1927,6 +1940,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __make_unsigned<__GLIBCXX_TYPE_INT_N_3>
     { using __type = unsigned __GLIBCXX_TYPE_INT_N_3; };
 #endif
+#if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__
+  __extension__
+  template<>
+    struct __make_unsigned<__int128>
+    { using __type = unsigned __int128; };
+#endif
 
   // Select between integral and enum: not possible to be both.
   template<typename _Tp,
@@ -2087,6 +2106,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __make_signed<unsigned __GLIBCXX_TYPE_INT_N_3>
     { using __type = __GLIBCXX_TYPE_INT_N_3; };
 #endif
+#if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__
+  __extension__
+  template<>
+    struct __make_signed<unsigned __int128>
+    { using __type = __int128; };
+#endif
 
   // Select between integral and enum: not possible to be both.
   template<typename _Tp,