mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 23:25:24 +02:00
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. Because the set of signed and 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. 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.B. 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 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. * 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. * include/c_compatibility/stdckdint.h: Replace all uses of the __cv_unqual_signed_or_unsigned_integer_type concept with __is_signed_or_unsigned_integer. (__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. 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): ... 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. Reviewed-by: Patrick Palka <ppalka@redhat.com>
151 lines
4.7 KiB
C++
151 lines
4.7 KiB
C++
// Saturation arithmetic -*- C++ -*-
|
|
|
|
// Copyright The GNU Toolchain Authors.
|
|
//
|
|
// 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.
|
|
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
/** @file include/bits/sat_arith.h
|
|
* This is an internal header file, included by other library headers.
|
|
* Do not attempt to use it directly. @headername{numeric}
|
|
*/
|
|
|
|
#ifndef _GLIBCXX_SAT_ARITH_H
|
|
#define _GLIBCXX_SAT_ARITH_H 1
|
|
|
|
#ifdef _GLIBCXX_SYSHDR
|
|
#pragma GCC system_header
|
|
#endif
|
|
|
|
#include <bits/version.h>
|
|
|
|
#ifdef __glibcxx_saturation_arithmetic // C++ >= 26
|
|
|
|
#include <concepts>
|
|
#include <ext/numeric_traits.h>
|
|
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
|
|
/// Add two integers, with saturation in case of overflow.
|
|
template<typename _Tp> requires __is_signed_or_unsigned_integer<_Tp>::value
|
|
constexpr _Tp
|
|
add_sat(_Tp __x, _Tp __y) noexcept
|
|
{
|
|
_Tp __z;
|
|
if (!__builtin_add_overflow(__x, __y, &__z))
|
|
return __z;
|
|
if constexpr (is_unsigned_v<_Tp>)
|
|
return __gnu_cxx::__int_traits<_Tp>::__max;
|
|
else if (__x < 0)
|
|
return __gnu_cxx::__int_traits<_Tp>::__min;
|
|
else
|
|
return __gnu_cxx::__int_traits<_Tp>::__max;
|
|
}
|
|
|
|
/// Subtract one integer from another, with saturation in case of overflow.
|
|
template<typename _Tp> requires __is_signed_or_unsigned_integer<_Tp>::value
|
|
constexpr _Tp
|
|
sub_sat(_Tp __x, _Tp __y) noexcept
|
|
{
|
|
_Tp __z;
|
|
if (!__builtin_sub_overflow(__x, __y, &__z))
|
|
return __z;
|
|
if constexpr (is_unsigned_v<_Tp>)
|
|
return __gnu_cxx::__int_traits<_Tp>::__min;
|
|
else if (__x < 0)
|
|
return __gnu_cxx::__int_traits<_Tp>::__min;
|
|
else
|
|
return __gnu_cxx::__int_traits<_Tp>::__max;
|
|
}
|
|
|
|
/// Multiply two integers, with saturation in case of overflow.
|
|
template<typename _Tp> requires __is_signed_or_unsigned_integer<_Tp>::value
|
|
constexpr _Tp
|
|
mul_sat(_Tp __x, _Tp __y) noexcept
|
|
{
|
|
_Tp __z;
|
|
if (!__builtin_mul_overflow(__x, __y, &__z))
|
|
return __z;
|
|
if constexpr (is_unsigned_v<_Tp>)
|
|
return __gnu_cxx::__int_traits<_Tp>::__max;
|
|
else if ((__x < 0) != (__y < 0))
|
|
return __gnu_cxx::__int_traits<_Tp>::__min;
|
|
else
|
|
return __gnu_cxx::__int_traits<_Tp>::__max;
|
|
}
|
|
|
|
/// Divide one integer by another, with saturation in case of overflow.
|
|
template<typename _Tp> requires __is_signed_or_unsigned_integer<_Tp>::value
|
|
constexpr _Tp
|
|
div_sat(_Tp __x, _Tp __y) noexcept
|
|
{
|
|
__glibcxx_assert(__y != 0);
|
|
if constexpr (is_signed_v<_Tp>)
|
|
if (__x == __gnu_cxx::__int_traits<_Tp>::__min && __y == _Tp(-1))
|
|
return __gnu_cxx::__int_traits<_Tp>::__max;
|
|
return __x / __y;
|
|
}
|
|
|
|
/// Divide one integer by another, with saturation in case of overflow.
|
|
template<typename _Res, typename _Tp>
|
|
requires __is_signed_or_unsigned_integer<_Res>::value
|
|
&& __is_signed_or_unsigned_integer<_Tp>::value
|
|
constexpr _Res
|
|
saturate_cast(_Tp __x) noexcept
|
|
{
|
|
constexpr int __digits_R = __gnu_cxx::__int_traits<_Res>::__digits;
|
|
constexpr int __digits_T = __gnu_cxx::__int_traits<_Tp>::__digits;
|
|
constexpr _Res __max_Res = __gnu_cxx::__int_traits<_Res>::__max;
|
|
|
|
if constexpr (is_signed_v<_Res> == is_signed_v<_Tp>)
|
|
{
|
|
if constexpr (__digits_R < __digits_T)
|
|
{
|
|
constexpr _Res __min_Res = __gnu_cxx::__int_traits<_Res>::__min;
|
|
|
|
if (__x < static_cast<_Tp>(__min_Res))
|
|
return __min_Res;
|
|
else if (__x > static_cast<_Tp>(__max_Res))
|
|
return __max_Res;
|
|
}
|
|
}
|
|
else if constexpr (is_signed_v<_Tp>) // Res is unsigned
|
|
{
|
|
if (__x < 0)
|
|
return 0;
|
|
else if (make_unsigned_t<_Tp>(__x) > __max_Res)
|
|
return __gnu_cxx::__int_traits<_Res>::__max;
|
|
}
|
|
else // Tp is unsigned, Res is signed
|
|
{
|
|
if (__x > make_unsigned_t<_Res>(__max_Res))
|
|
return __max_Res;
|
|
}
|
|
return static_cast<_Res>(__x);
|
|
}
|
|
|
|
_GLIBCXX_END_NAMESPACE_VERSION
|
|
} // namespace
|
|
|
|
#endif // __glibcxx_saturation_arithmetic
|
|
#endif /* _GLIBCXX_SAT_ARITH_H */
|