On Mon, 8 Dec 2025, Jonathan Wakely wrote:
> We have __is_signed_integer and __is_unsigned_integer traits which
> should have been updated by r16-2190-g4faa42ac0dee2c when making
> __int128 an extended integer type (for PR libstdc++/96710). Currently
> they check whether the type is a signed integer type or an unsigned
> integer type, or a cv-qualified version of one of those. This doesn't
> match the standard's definition, which does not include cv-qualified
> types. This change ensures that signed __int128 and unsigned __int128
> are included in those traits in strict -std modes, and it removes the
> use of remove_cv_t so that they are not true for cv-qualified types.
> This makes the traits match the meaning of "signed integer type" and
> "unsigned integer type" in the standard ([basic.fundamental]).
>
> We also have an __is_standard_integer trait, which is true if either
> __is_signed_integer or __is_unsigned_integer is true, but that's also
> not a match for the definition in the standard. The definitions of
> "signed integer type" and "unsigned integer type" include both standard
> and extended integer types, so only saying "standard" in the trait name
> is misleading (even before this change, because in non-strict -std modes
> the __GLIBCXX_TYPE_INT_N_0 .. __GLIBCXX_TYPE_INT_N_3 types were always
> included in the trait, and they aren't standard integer types).
>
> This change renames __is_standard_integer to the more accurate
> __is_signed_or_unsigned_integer_type. Because the set of signed and
In the code __is_signed_or_unsigned_integer is the new name, no
_type suffix? Besides that LGTM, this is a nice improvement.
Btw we probably should define __is_signed/unsigned_integer via explicit
specializations instead of the __is_one_of helper, so that it's O(1)
instead of O(n) to check.
> unsigned integer types is the same as the set of standard and extended
> integer types, the trait could instead have been renamed to
> __is_standard_or_extended_integer_type. I think it's clearer and more
> self-explanatory to avoid "standard and extended" and name it for the
> signed and unsigned integer types.
>
> N.N. we don't want to call it just __is_integer_type because the integer
> types includes cv-qualified types and also bool and the character types
> char, wchar_t, char16_t etc.
>
> The consequences of redefining and renaming these traits are small, and
> only positive.
>
> Apart from the uses in the __is_standard_integer trait, the only other
> uses of __is_signed_integer and __is_unsigned_integer are in <format>
> and those uses are unaffected by this change to add 128-bit integers to
> the traits. In both uses the type argument is already cv-unqualified,
> and there is already explicit handling for 128-bit integers where that
> is required.
>
> The existing uses of __is_standard_integer can simply be changed to use
> the new name. This does change the behaviour of those uses of the trait,
> because the __is_signed_or_unsigned_integer_type trait now includes
> 128-bit integers in strict modes. However, that is a desirable change
> that fixes some bugs. Specifically, the [utility.intcmp] functions such
> as std::cmp_less and the [numeric.sat.arith] functions such as
> std::add_sat did not support 128-bit integers in strict modes. Since the
> standard says they should be enabled for all signed and unsigned integer
> types (or equivalently, for all standard and extended integer types),
> those functions should all support __int128 and unsigned __int128. That
> is fixed by this change. Additionally, the same changes in <charconv>,
> <mdspan>, and <stdckdint.h> enable the use of 128-bit integers for those
> APIs in strict modes.
>
> Finally, this also make a drive-by fix to the enable_if constraints for
> the integer overloads of std::from_chars. That used remove_cv_t and so
> enabled the overload for lvalue arguments of type const char, which
> won't work and should not be enabled.
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/intcmp.h: Replace all uses of
> __is_standard_integer with __is_signed_or_unsigned_integer_type.
> * include/bits/max_size_type.h: Fix outdated comment.
> * include/bits/sat_arith.h: Replace all uses of
> __is_standard_integer with __is_signed_or_unsigned_integer_type.
> * include/c_compatibility/stdckdint.h: Replace all uses of the
> __cv_unqual_signed_or_unsigned_integer_type concept with
> __is_signed_or_unsigned_integer_type.
> (__cv_unqual_signed_or_unsigned_integer_type): Remove.
> * include/ext/numeric_traits.h: Fix outdated comment.
> * include/std/charconv (from_chars): Replace use of
> __is_standard_integer with __is_signed_or_unsigned_integer_type.
> Do not enable for cv-qualified char.
> * include/std/mdspan: Likewise.
> * include/std/type_traits (__is_unsigned_integer): Include
> unsigned __int128 in type list.
> (__is_signed_integer): Include signed __int128 in type list.
> (__is_standard_integer): Rename to ...
> (__is_signed_or_unsigned_integer_type): ... this.
> * testsuite/23_containers/mdspan/extents/ctor_ints.cc: Test
> with 128-bit integers.
> * testsuite/23_containers/mdspan/submdspan/strided_slice.cc:
> Likewise.
> * testsuite/20_util/integer_comparisons/extended.cc: New test.
> * testsuite/26_numerics/saturation/extended.cc: New test.
> * testsuite/26_numerics/stdckdint/extended.cc: New test.
> ---
>
> Tested x86_64-linux.
>
> libstdc++-v3/include/bits/intcmp.h | 12 ++--
> libstdc++-v3/include/bits/max_size_type.h | 6 +-
> libstdc++-v3/include/bits/sat_arith.h | 12 ++--
> .../include/c_compatibility/stdckdint.h | 32 +++------
> libstdc++-v3/include/ext/numeric_traits.h | 2 +-
> libstdc++-v3/include/std/charconv | 4 +-
> libstdc++-v3/include/std/mdspan | 8 +--
> libstdc++-v3/include/std/type_traits | 13 +++-
> .../20_util/integer_comparisons/extended.cc | 60 +++++++++++++++++
> .../23_containers/mdspan/extents/ctor_ints.cc | 7 ++
> .../mdspan/submdspan/strided_slice.cc | 3 +
> .../26_numerics/saturation/extended.cc | 55 ++++++++++++++++
> .../26_numerics/stdckdint/extended.cc | 65 +++++++++++++++++++
> 13 files changed, 231 insertions(+), 48 deletions(-)
> create mode 100644
> libstdc++-v3/testsuite/20_util/integer_comparisons/extended.cc
> create mode 100644 libstdc++-v3/testsuite/26_numerics/saturation/extended.cc
> create mode 100644 libstdc++-v3/testsuite/26_numerics/stdckdint/extended.cc
>
> diff --git a/libstdc++-v3/include/bits/intcmp.h
> b/libstdc++-v3/include/bits/intcmp.h
> index 3d3fbc43d2c8..bb9c7f2c7ff6 100644
> --- a/libstdc++-v3/include/bits/intcmp.h
> +++ b/libstdc++-v3/include/bits/intcmp.h
> @@ -49,8 +49,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> constexpr bool
> cmp_equal(_Tp __t, _Up __u) noexcept
> {
> - static_assert(__is_standard_integer<_Tp>::value);
> - static_assert(__is_standard_integer<_Up>::value);
> + static_assert(__is_signed_or_unsigned_integer<_Tp>::value);
> + static_assert(__is_signed_or_unsigned_integer<_Up>::value);
>
> if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
> return __t == __u;
> @@ -69,8 +69,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> constexpr bool
> cmp_less(_Tp __t, _Up __u) noexcept
> {
> - static_assert(__is_standard_integer<_Tp>::value);
> - static_assert(__is_standard_integer<_Up>::value);
> + static_assert(__is_signed_or_unsigned_integer<_Tp>::value);
> + static_assert(__is_signed_or_unsigned_integer<_Up>::value);
>
> if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
> return __t < __u;
> @@ -99,8 +99,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> constexpr bool
> in_range(_Tp __t) noexcept
> {
> - static_assert(__is_standard_integer<_Res>::value);
> - static_assert(__is_standard_integer<_Tp>::value);
> + static_assert(__is_signed_or_unsigned_integer<_Res>::value);
> + static_assert(__is_signed_or_unsigned_integer<_Tp>::value);
> using __gnu_cxx::__int_traits;
>
> if constexpr (is_signed_v<_Tp> == is_signed_v<_Res>)
> diff --git a/libstdc++-v3/include/bits/max_size_type.h
> b/libstdc++-v3/include/bits/max_size_type.h
> index a34b91a04f1e..537aceeeca02 100644
> --- a/libstdc++-v3/include/bits/max_size_type.h
> +++ b/libstdc++-v3/include/bits/max_size_type.h
> @@ -44,10 +44,8 @@
> // [iterator.concept.winc]) that are one bit wider than the widest supported
> // integer type.
> //
> -// The set of integer types we consider includes __int128 and unsigned
> __int128
> -// (when they exist), even though they are really integer types only in GNU
> -// mode. This is to obtain a consistent ABI for these integer-class types
> -// across strict mode and GNU mode.
> +// The set of integer types we consider includes the extended integer types
> +// __int128 and unsigned __int128 (when they exist).
>
> namespace std _GLIBCXX_VISIBILITY(default)
> {
> diff --git a/libstdc++-v3/include/bits/sat_arith.h
> b/libstdc++-v3/include/bits/sat_arith.h
> index e036fc88e40b..bce86d919919 100644
> --- a/libstdc++-v3/include/bits/sat_arith.h
> +++ b/libstdc++-v3/include/bits/sat_arith.h
> @@ -46,7 +46,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
> _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> /// Add two integers, with saturation in case of overflow.
> - template<typename _Tp> requires __is_standard_integer<_Tp>::value
> + template<typename _Tp> requires __is_signed_or_unsigned_integer<_Tp>::value
> constexpr _Tp
> add_sat(_Tp __x, _Tp __y) noexcept
> {
> @@ -62,7 +62,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> /// Subtract one integer from another, with saturation in case of overflow.
> - template<typename _Tp> requires __is_standard_integer<_Tp>::value
> + template<typename _Tp> requires __is_signed_or_unsigned_integer<_Tp>::value
> constexpr _Tp
> sub_sat(_Tp __x, _Tp __y) noexcept
> {
> @@ -78,7 +78,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> /// Multiply two integers, with saturation in case of overflow.
> - template<typename _Tp> requires __is_standard_integer<_Tp>::value
> + template<typename _Tp> requires __is_signed_or_unsigned_integer<_Tp>::value
> constexpr _Tp
> mul_sat(_Tp __x, _Tp __y) noexcept
> {
> @@ -94,7 +94,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> /// Divide one integer by another, with saturation in case of overflow.
> - template<typename _Tp> requires __is_standard_integer<_Tp>::value
> + template<typename _Tp> requires __is_signed_or_unsigned_integer<_Tp>::value
> constexpr _Tp
> div_sat(_Tp __x, _Tp __y) noexcept
> {
> @@ -107,8 +107,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> /// Divide one integer by another, with saturation in case of overflow.
> template<typename _Res, typename _Tp>
> - requires __is_standard_integer<_Res>::value
> - && __is_standard_integer<_Tp>::value
> + requires __is_signed_or_unsigned_integer<_Res>::value
> + && __is_signed_or_unsigned_integer<_Tp>::value
> constexpr _Res
> saturate_cast(_Tp __x) noexcept
> {
> diff --git a/libstdc++-v3/include/c_compatibility/stdckdint.h
> b/libstdc++-v3/include/c_compatibility/stdckdint.h
> index 1de2d18dc1aa..5bdf4dc7b24b 100644
> --- a/libstdc++-v3/include/c_compatibility/stdckdint.h
> +++ b/libstdc++-v3/include/c_compatibility/stdckdint.h
> @@ -40,15 +40,6 @@
> namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
> {
> #endif
> -/// @cond undocumented
> -namespace __detail
> -{
> - template<typename _Tp>
> - concept __cv_unqual_signed_or_unsigned_integer_type
> - = std::same_as<_Tp, std::remove_cv_t<_Tp>>
> - && std::__is_standard_integer<_Tp>::value;
> -}
> -/// @endcond
>
> /** Checked integer arithmetic
> *
> @@ -71,10 +62,9 @@ template<typename _Tp1, typename _Tp2, typename _Tp3>
> inline bool
> ckd_add(_Tp1* __result, _Tp2 __a, _Tp3 __b)
> {
> - using __gnu_cxx::__detail::__cv_unqual_signed_or_unsigned_integer_type;
> - static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp1>);
> - static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp2>);
> - static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp3>);
> + static_assert(std::__is_signed_or_unsigned_integer<_Tp1>::value);
> + static_assert(std::__is_signed_or_unsigned_integer<_Tp2>::value);
> + static_assert(std::__is_signed_or_unsigned_integer<_Tp3>::value);
> return __builtin_add_overflow(__a, __b, __result);
> }
>
> @@ -82,10 +72,9 @@ template<typename _Tp1, typename _Tp2, typename _Tp3>
> inline bool
> ckd_sub(_Tp1* __result, _Tp2 __a, _Tp3 __b)
> {
> - using __gnu_cxx::__detail::__cv_unqual_signed_or_unsigned_integer_type;
> - static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp1>);
> - static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp2>);
> - static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp3>);
> + static_assert(std::__is_signed_or_unsigned_integer<_Tp1>::value);
> + static_assert(std::__is_signed_or_unsigned_integer<_Tp2>::value);
> + static_assert(std::__is_signed_or_unsigned_integer<_Tp3>::value);
> return __builtin_sub_overflow(__a, __b, __result);
> }
>
> @@ -93,15 +82,14 @@ template<typename _Tp1, typename _Tp2, typename _Tp3>
> inline bool
> ckd_mul(_Tp1* __result, _Tp2 __a, _Tp3 __b)
> {
> - using __gnu_cxx::__detail::__cv_unqual_signed_or_unsigned_integer_type;
> - static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp1>);
> - static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp2>);
> - static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp3>);
> + static_assert(std::__is_signed_or_unsigned_integer<_Tp1>::value);
> + static_assert(std::__is_signed_or_unsigned_integer<_Tp2>::value);
> + static_assert(std::__is_signed_or_unsigned_integer<_Tp3>::value);
> return __builtin_mul_overflow(__a, __b, __result);
> }
> /// @}
> #ifndef _GLIBCXX_DOXYGEN
> -}
> +} // namespace __gnu_cxx
>
> using __gnu_cxx::ckd_add;
> using __gnu_cxx::ckd_sub;
> diff --git a/libstdc++-v3/include/ext/numeric_traits.h
> b/libstdc++-v3/include/ext/numeric_traits.h
> index 6786dc6a71bc..78cb8e398756 100644
> --- a/libstdc++-v3/include/ext/numeric_traits.h
> +++ b/libstdc++-v3/include/ext/numeric_traits.h
> @@ -48,7 +48,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // or is_signed, digits10, max_digits10, or max_exponent10 for floats.
>
> // Unlike __is_integer (and std::is_integral) this trait is true for
> - // non-standard built-in integer types such as __int128 and __int20.
> + // non-standard built-in integer types such as __int20.
> template<typename _Tp>
> struct __is_integer_nonstrict
> : public std::__is_integer<_Tp>
> diff --git a/libstdc++-v3/include/std/charconv
> b/libstdc++-v3/include/std/charconv
> index 8cf2c0b01d7c..cc513cd8c226 100644
> --- a/libstdc++-v3/include/std/charconv
> +++ b/libstdc++-v3/include/std/charconv
> @@ -556,8 +556,8 @@ namespace __detail
>
> /// std::from_chars for integral types.
> template<typename _Tp,
> - enable_if_t<__or_<__is_standard_integer<_Tp>,
> - is_same<char, remove_cv_t<_Tp>>>::value, int> = 0>
> + enable_if_t<__or_<__is_signed_or_unsigned_integer<_Tp>,
> + is_same<char, _Tp>>::value, int> = 0>
> _GLIBCXX23_CONSTEXPR from_chars_result
> from_chars(const char* __first, const char* __last, _Tp& __value,
> int __base = 10)
> diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
> index 03cc4f02a1cd..981fa1c601a1 100644
> --- a/libstdc++-v3/include/std/mdspan
> +++ b/libstdc++-v3/include/std/mdspan
> @@ -352,11 +352,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _OffsetType, typename _ExtentType, typename _StrideType>
> struct strided_slice
> {
> - static_assert(__is_standard_integer<_OffsetType>::value
> + static_assert(__is_signed_or_unsigned_integer<_OffsetType>::value
> || __detail::__integral_constant_like<_OffsetType>);
> - static_assert(__is_standard_integer<_ExtentType>::value
> + static_assert(__is_signed_or_unsigned_integer<_ExtentType>::value
> || __detail::__integral_constant_like<_ExtentType>);
> - static_assert(__is_standard_integer<_StrideType>::value
> + static_assert(__is_signed_or_unsigned_integer<_StrideType>::value
> || __detail::__integral_constant_like<_StrideType>);
>
> using offset_type = _OffsetType;
> @@ -379,7 +379,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _IndexType, size_t... _Extents>
> class extents
> {
> - static_assert(__is_standard_integer<_IndexType>::value,
> + static_assert(__is_signed_or_unsigned_integer<_IndexType>::value,
> "IndexType must be a signed or unsigned integer type");
> static_assert(
> (__mdspan::__valid_static_extent<_Extents, _IndexType> && ...),
> diff --git a/libstdc++-v3/include/std/type_traits
> b/libstdc++-v3/include/std/type_traits
> index 7c157ea7ba19..39b447b7e17e 100644
> --- a/libstdc++-v3/include/std/type_traits
> +++ b/libstdc++-v3/include/std/type_traits
> @@ -826,7 +826,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // Check if a type is one of the signed integer types.
> __extension__
> template<typename _Tp>
> - using __is_signed_integer = __is_one_of<__remove_cv_t<_Tp>,
> + using __is_signed_integer = __is_one_of<_Tp,
> signed char, signed short, signed int, signed long,
> signed long long
> #if defined(__GLIBCXX_TYPE_INT_N_0)
> @@ -840,13 +840,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #endif
> #if defined(__GLIBCXX_TYPE_INT_N_3)
> , signed __GLIBCXX_TYPE_INT_N_3
> +#endif
> +#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
> + , signed __int128
> #endif
> >;
>
> // Check if a type is one of the unsigned integer types.
> __extension__
> template<typename _Tp>
> - using __is_unsigned_integer = __is_one_of<__remove_cv_t<_Tp>,
> + using __is_unsigned_integer = __is_one_of<_Tp,
> unsigned char, unsigned short, unsigned int, unsigned long,
> unsigned long long
> #if defined(__GLIBCXX_TYPE_INT_N_0)
> @@ -860,12 +863,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #endif
> #if defined(__GLIBCXX_TYPE_INT_N_3)
> , unsigned __GLIBCXX_TYPE_INT_N_3
> +#endif
> +#if defined __STRICT_ANSI__ && defined __SIZEOF_INT128__
> + , unsigned __int128
> #endif
> >;
>
> // Check if a type is one of the signed or unsigned integer types.
> + // i.e. an integral types except bool, char, wchar_t, and charN_t.
> template<typename _Tp>
> - using __is_standard_integer
> + using __is_signed_or_unsigned_integer
> = __or_<__is_signed_integer<_Tp>, __is_unsigned_integer<_Tp>>;
>
> // __void_t (std::void_t for C++11)
> diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/extended.cc
> b/libstdc++-v3/testsuite/20_util/integer_comparisons/extended.cc
> new file mode 100644
> index 000000000000..d862b16b369b
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/extended.cc
> @@ -0,0 +1,60 @@
> +// { dg-do compile { target c++20 } }
> +
> +#include <utility>
> +
> +template<typename T>
> +constexpr bool
> +test()
> +{
> + using S = std::make_signed_t<T>;
> + using U = std::make_unsigned_t<T>;
> +
> + static_assert( std::cmp_less((S)-1, (U)1));
> + static_assert( ! std::cmp_less((S)20, (U)10));
> + static_assert( ! std::cmp_less((U)20, (S)10));
> +
> + static_assert( std::cmp_greater((S)100, (U)1) );
> + static_assert( std::cmp_greater((U)100, (S)1) );
> + static_assert( ! std::cmp_greater((S)-100, (U)1) );
> +
> + static_assert( std::cmp_less_equal((S)-1, (U)1));
> + static_assert( std::cmp_less_equal((U)10, (S)10));
> + static_assert( ! std::cmp_less_equal((U)-100, (S)-100));
> +
> + static_assert( std::cmp_greater_equal((S)200, (U)2) );
> + static_assert( std::cmp_greater_equal((U)2000, (S)2000) );
> + static_assert( ! std::cmp_greater_equal((S)-100, (U)100) );
> +
> + static_assert( std::cmp_equal((S)1, (U)1) );
> + static_assert( ! std::cmp_equal((S)-2, (U)-2) );
> +
> + static_assert( std::cmp_not_equal((S)-1, (U)-1) );
> + static_assert( ! std::cmp_not_equal((S)100, (U)100) );
> +
> + static_assert( std::in_range<S>((U)5) );
> + static_assert( std::in_range<U>((S)5) );
> + static_assert( ! std::in_range<S>((U)-5) );
> + static_assert( ! std::in_range<U>((S)-5) );
> +
> + return true;
> +}
> +
> +#ifdef __SIZEOF_INT128__
> +static_assert(test<__int128>());
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_0
> +static_assert(test<__GLIBCXX_TYPE_INT_N_0>());
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_1
> +static_assert(test<__GLIBCXX_TYPE_INT_N_1>());
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_2
> +static_assert(test<__GLIBCXX_TYPE_INT_N_2>());
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_3
> +static_assert(test<__GLIBCXX_TYPE_INT_N_3>());
> +#endif
> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc
> b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc
> index d5f07c106aab..fdbcb707bbe0 100644
> --- a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc
> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc
> @@ -16,6 +16,13 @@ static_assert(!std::is_constructible_v<std::extents<int,
> 1, dyn, 3>, int, int>);
> // Not constructible from non integer-like objects.
> static_assert(!std::is_constructible_v<std::extents<int, 1>, int, A>);
>
> +#ifdef __SIZEOF_INT128__
> +static_assert(std::is_constructible_v<std::extents<__int128, 1, 2>,
> + __int128, unsigned __int128>);
> +static_assert(std::is_constructible_v<std::extents<unsigned __int128, 1, 2>,
> + unsigned int, int>);
> +#endif
> +
> // No implicit conversion from integer-like objects.
> template<typename Extent, typename... OExtents>
> constexpr bool
> diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/strided_slice.cc
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/strided_slice.cc
> index c43a82143214..6fa5aaa2f1ed 100644
> --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/strided_slice.cc
> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/strided_slice.cc
> @@ -34,6 +34,9 @@ test_all()
> test_initializers(0, 1, 2);
> test_initializers(std::integral_constant<short, 0>{}, size_t{1},
> std::cw<2>);
> test_initializers(-1, 2, 2);
> +#ifdef __SIZEOF_INT128__
> + test_initializers((__int128)1, (unsigned __int128)-2,
> std::cw<(__int128)3>);
> +#endif
> return true;
> }
>
> diff --git a/libstdc++-v3/testsuite/26_numerics/saturation/extended.cc
> b/libstdc++-v3/testsuite/26_numerics/saturation/extended.cc
> new file mode 100644
> index 000000000000..fbef62826c23
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/26_numerics/saturation/extended.cc
> @@ -0,0 +1,55 @@
> +// { dg-do compile { target c++26 } }
> +
> +#include <numeric>
> +#include <limits>
> +
> +template<typename T>
> +constexpr bool
> +test()
> +{
> + using S = std::make_signed_t<T>;
> + using U = std::make_unsigned_t<T>;
> +
> + constexpr S smax = std::numeric_limits<S>::max();
> + constexpr S smin = std::numeric_limits<S>::min();
> + constexpr U umax = std::numeric_limits<U>::max();
> +
> + static_assert( std::add_sat(smax, (S)1) == smax );
> + static_assert( std::add_sat(smin, (S)-2) == smin );
> + static_assert( std::add_sat(umax, (U)3) == umax );
> +
> + static_assert( std::sub_sat(smax, (S)-1) == smax );
> + static_assert( std::sub_sat(smin, (S)2) == smin );
> + static_assert( std::sub_sat((U)0, (U)3) == (U)0 );
> +
> + static_assert( std::mul_sat((S)(smax >> 1), (S)3) == smax );
> + static_assert( std::mul_sat((S)(smin >> 1), (S)5) == smin );
> + static_assert( std::mul_sat((U)(umax >> 1), (U)7) == umax );
> +
> + static_assert( std::div_sat(smax, (S)2) == (smax >> 1) );
> + static_assert( std::div_sat(smin, (S)4) == (smin >> 2) );
> + static_assert( std::div_sat(smin, (S)-1) == smax );
> + static_assert( std::div_sat(umax, (U)8) == (umax >> 3) );
> +
> + return true;
> +}
> +
> +#ifdef __SIZEOF_INT128__
> +static_assert(test<__int128>());
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_0
> +static_assert(test<__GLIBCXX_TYPE_INT_N_0>());
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_1
> +static_assert(test<__GLIBCXX_TYPE_INT_N_1>());
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_2
> +static_assert(test<__GLIBCXX_TYPE_INT_N_2>());
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_3
> +static_assert(test<__GLIBCXX_TYPE_INT_N_3>());
> +#endif
> diff --git a/libstdc++-v3/testsuite/26_numerics/stdckdint/extended.cc
> b/libstdc++-v3/testsuite/26_numerics/stdckdint/extended.cc
> new file mode 100644
> index 000000000000..efc07923ac07
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/26_numerics/stdckdint/extended.cc
> @@ -0,0 +1,65 @@
> +// { dg-do run { target c++26 } }
> +
> +#include <stdckdint.h>
> +#include <limits>
> +#include <testsuite_hooks.h>
> +
> +template<typename T>
> +void
> +test()
> +{
> + using S = std::make_signed_t<T>;
> + using U = std::make_unsigned_t<T>;
> +
> + constexpr S smax = std::numeric_limits<S>::max();
> + constexpr S smin = std::numeric_limits<S>::min();
> + constexpr U umax = std::numeric_limits<U>::max();
> + S sout{};
> + U uout{};
> +
> + VERIFY( ckd_add(&sout, smax, (S)1) );
> + VERIFY( ! ckd_add(&uout, smax, (U)1) && uout == ((U)smax + (U)1) );
> + VERIFY( ! ckd_add(&sout, smin, (S)99) && sout == (smin + 99) );
> + VERIFY( ckd_add(&uout, smin, (S)99) );
> + VERIFY( ckd_add(&sout, smin, (S)-2) );
> + VERIFY( ckd_add(&uout, umax, (U)3) );
> + VERIFY( ! ckd_add(&sout, (U)9, (U)3) && sout == 12 );
> +
> + VERIFY( ckd_sub(&sout, smax, (S)-1) );
> + VERIFY( ! ckd_sub(&uout, smax, (S)-1) && uout == ((U)smax + (U)1) );
> + VERIFY( ckd_sub(&sout, smin, (S)2) );
> + VERIFY( ! ckd_sub(&sout, smin, (S)-2) && sout == (smin + 2) );
> + VERIFY( ! ckd_sub(&sout, (U)0, (U)3) && sout == -3 );
> + VERIFY( ckd_sub(&uout, (U)0, (U)3) );
> +
> + VERIFY( ! ckd_mul(&sout, (S)(smax >> 2), (S)3) && sout == (smax/4*3) );
> + VERIFY( ckd_mul(&sout, (S)(smax >> 1), (S)3) );
> + VERIFY( ! ckd_mul(&uout, (S)(smax >> 1), (S)3) );
> + VERIFY( ckd_mul(&sout, (S)(smin >> 1), (S)5) );
> + VERIFY( ! ckd_mul(&uout, (U)(umax >> 2), (U)3) );
> + VERIFY( ckd_mul(&sout, (U)(umax >> 2), (U)3) );
> + VERIFY( ckd_mul(&uout, (U)(umax >> 1), (U)7) );
> +}
> +
> +int main()
> +{
> +#ifdef __SIZEOF_INT128__
> + test<__int128>();
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_0
> + test<__GLIBCXX_TYPE_INT_N_0>();
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_1
> + test<__GLIBCXX_TYPE_INT_N_1>();
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_2
> + test<__GLIBCXX_TYPE_INT_N_2>();
> +#endif
> +
> +#ifdef __GLIBCXX_TYPE_INT_N_3
> + test<__GLIBCXX_TYPE_INT_N_3>();
> +#endif
> +}
> --
> 2.52.0
>
>