On Wed, 9 Jul 2025, Jonathan Wakely wrote: > 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).
Yay! LGTM > > 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. > --- > > Tested x86_64-linux and powerpc64le-linux. > > libstdc++-v3/include/bits/cpp_type_traits.h | 17 +++---- > libstdc++-v3/include/bits/iterator_concepts.h | 34 ------------- > libstdc++-v3/include/bits/max_size_type.h | 48 +++++++++---------- > libstdc++-v3/include/bits/ranges_base.h | 15 ------ > libstdc++-v3/include/bits/stl_algobase.h | 7 +++ > libstdc++-v3/include/ext/numeric_traits.h | 6 --- > libstdc++-v3/include/std/charconv | 4 ++ > libstdc++-v3/include/std/format | 14 ------ > libstdc++-v3/include/std/limits | 3 +- > libstdc++-v3/include/std/type_traits | 25 ++++++++++ > 10 files changed, 67 insertions(+), 106 deletions(-) > > diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h > b/libstdc++-v3/include/bits/cpp_type_traits.h > index b1a6206ce1eb..770ad94b3b4d 100644 > --- a/libstdc++-v3/include/bits/cpp_type_traits.h > +++ b/libstdc++-v3/include/bits/cpp_type_traits.h > @@ -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 }; }; > diff --git a/libstdc++-v3/include/bits/iterator_concepts.h > b/libstdc++-v3/include/bits/iterator_concepts.h > index d31e4f145107..979039e7da53 100644 > --- a/libstdc++-v3/include/bits/iterator_concepts.h > +++ b/libstdc++-v3/include/bits/iterator_concepts.h > @@ -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 > diff --git a/libstdc++-v3/include/bits/max_size_type.h > b/libstdc++-v3/include/bits/max_size_type.h > index 30c5b1247679..a34b91a04f1e 100644 > --- a/libstdc++-v3/include/bits/max_size_type.h > +++ b/libstdc++-v3/include/bits/max_size_type.h > @@ -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)); } > diff --git a/libstdc++-v3/include/bits/ranges_base.h > b/libstdc++-v3/include/bits/ranges_base.h > index c09f7292067d..0251e5d0928a 100644 > --- a/libstdc++-v3/include/bits/ranges_base.h > +++ b/libstdc++-v3/include/bits/ranges_base.h > @@ -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); > } > diff --git a/libstdc++-v3/include/bits/stl_algobase.h > b/libstdc++-v3/include/bits/stl_algobase.h > index 4d5662ca45bf..71ef2335a311 100644 > --- a/libstdc++-v3/include/bits/stl_algobase.h > +++ b/libstdc++-v3/include/bits/stl_algobase.h > @@ -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 > diff --git a/libstdc++-v3/include/ext/numeric_traits.h > b/libstdc++-v3/include/ext/numeric_traits.h > index 2cd89430f51c..6786dc6a71bc 100644 > --- a/libstdc++-v3/include/ext/numeric_traits.h > +++ b/libstdc++-v3/include/ext/numeric_traits.h > @@ -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 > diff --git a/libstdc++-v3/include/std/charconv > b/libstdc++-v3/include/std/charconv > index dda49ce72d0b..8cf2c0b01d7c 100644 > --- a/libstdc++-v3/include/std/charconv > +++ b/libstdc++-v3/include/std/charconv > @@ -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 > diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format > index d584b81c78a1..d63c6fc9efd5 100644 > --- a/libstdc++-v3/include/std/format > +++ b/libstdc++-v3/include/std/format > @@ -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{}; > }; > > diff --git a/libstdc++-v3/include/std/limits b/libstdc++-v3/include/std/limits > index 2331c25599a0..3567a3284006 100644 > --- a/libstdc++-v3/include/std/limits > +++ b/libstdc++-v3/include/std/limits > @@ -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)) > diff --git a/libstdc++-v3/include/std/type_traits > b/libstdc++-v3/include/std/type_traits > index 055411195f17..e88d04e44d76 100644 > --- a/libstdc++-v3/include/std/type_traits > +++ b/libstdc++-v3/include/std/type_traits > @@ -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, > -- > 2.50.0 > >