[libc++] std::abs support for _BitInt(N) and __int128 (#196532)
`std::abs` does not accept `__int128` or signed `_BitInt(N)`: the call is ambiguous and fails to compile (#204212). This adds an explicit `abs(__int128_t)` overload and an `abs(_BitInt(N))` overload that deduces the width, so every signed `_BitInt` gets a same-type result. `_BitInt` does not integer-promote, so without this overload a narrow signed `_BitInt` would be an ambiguous call against `abs(int/long/long long)` instead of promoting the way `signed char` and `short` do. Standard narrow types are unchanged: they still go through `abs(int)`. Part of the [_BitInt(N) libc++ effort](https://discourse.llvm.org/t/bitint-n-support-in-libc-investigations-possible-improvements-looking-for-guidance/90063). Fixes #204212 Assisted-by: Claude (Anthropic) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a88b88bdd3
commit
20cf19f0c3
2 changed files with 112 additions and 0 deletions
|
|
@ -63,6 +63,19 @@ template <class = int>
|
|||
return __builtin_llabs(__x);
|
||||
}
|
||||
|
||||
#if _LIBCPP_HAS_INT128
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline __int128_t abs(__int128_t __x) _NOEXCEPT { return __x < 0 ? -__x : __x; }
|
||||
#endif
|
||||
|
||||
#if defined(__BITINT_MAXWIDTH__)
|
||||
// _BitInt does not integer-promote, so without a same-type overload a narrow
|
||||
// signed _BitInt would be an ambiguous call against abs(int/long/long long).
|
||||
template <int _Np>
|
||||
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _BitInt(_Np) abs(_BitInt(_Np) __x) _NOEXCEPT {
|
||||
return __x < 0 ? -__x : __x;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace __math
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
|
||||
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
|
@ -37,6 +39,65 @@ void test_big() {
|
|||
assert(std::abs(negative_big_value) == big_value); // make sure it doesn't get casted to a smaller type
|
||||
}
|
||||
|
||||
// std::abs has __int128/_BitInt(N) overloads as a libc++ extension.
|
||||
// Narrow signed types stay on the abs(int) promotion path.
|
||||
template <class T>
|
||||
struct unpromoted_abs : std::is_same<decltype(std::abs(T(0))), T> {};
|
||||
static_assert(!unpromoted_abs<signed char>::value, "");
|
||||
static_assert(!unpromoted_abs<short>::value, "");
|
||||
|
||||
#if TEST_HAS_BITINT
|
||||
// Every signed _BitInt(N) gets a same-type abs, including widths narrower than
|
||||
// int. unsigned _BitInt is excluded by the signed-only contract.
|
||||
template <class T, class = void>
|
||||
struct has_abs : std::false_type {};
|
||||
template <class T>
|
||||
struct has_abs<T, decltype((void)std::abs(T(0)))> : std::true_type {};
|
||||
static_assert(has_abs<signed _BitInt(2)>::value, "");
|
||||
static_assert(has_abs<signed _BitInt(7)>::value, "");
|
||||
static_assert(has_abs<signed _BitInt(32)>::value, "");
|
||||
static_assert(has_abs<signed _BitInt(64)>::value, "");
|
||||
static_assert(!has_abs<unsigned _BitInt(32)>::value, ""); // signed-only contract
|
||||
static_assert(!has_abs<unsigned _BitInt(64)>::value, "");
|
||||
|
||||
template <int N>
|
||||
void test_signed_bitint() {
|
||||
typedef signed _BitInt(N) T;
|
||||
ASSERT_SAME_TYPE(decltype(std::abs(T(0))), T);
|
||||
assert(std::abs(T(0)) == T(0));
|
||||
assert(std::abs(T(1)) == T(1));
|
||||
assert(std::abs(T(42)) == T(42));
|
||||
assert(std::abs(T(-1)) == T(1));
|
||||
assert(std::abs(T(-42)) == T(42));
|
||||
|
||||
// T_MIN omitted: -T_MIN is UB, same as abs(INT_MIN).
|
||||
T t_max = static_cast<T>(~static_cast<unsigned _BitInt(N)>(0) >> 1);
|
||||
T t_min_plus1 = -t_max;
|
||||
assert(std::abs(t_max) == t_max);
|
||||
assert(std::abs(t_min_plus1) == t_max);
|
||||
}
|
||||
#endif // TEST_HAS_BITINT
|
||||
|
||||
#ifndef TEST_HAS_NO_INT128
|
||||
void test_int128() {
|
||||
ASSERT_SAME_TYPE(decltype(std::abs(static_cast<__int128_t>(0))), __int128_t);
|
||||
assert(std::abs(static_cast<__int128_t>(0)) == 0);
|
||||
assert(std::abs(static_cast<__int128_t>(42)) == 42);
|
||||
assert(std::abs(static_cast<__int128_t>(-42)) == 42);
|
||||
assert(std::abs(static_cast<__int128_t>(-1)) == 1);
|
||||
|
||||
// INT128_MIN omitted: -__x is UB, same as abs(INT_MIN).
|
||||
__int128_t big_neg = -((static_cast<__int128_t>(1) << 100));
|
||||
__int128_t big_pos = static_cast<__int128_t>(1) << 100;
|
||||
assert(std::abs(big_neg) == big_pos);
|
||||
|
||||
__int128_t int128_max = static_cast<__int128_t>(~static_cast<__uint128_t>(0) >> 1);
|
||||
__int128_t int128_min_plus1 = -int128_max;
|
||||
assert(std::abs(int128_max) == int128_max);
|
||||
assert(std::abs(int128_min_plus1) == int128_max);
|
||||
}
|
||||
#endif // TEST_HAS_NO_INT128
|
||||
|
||||
// The following is helpful to keep in mind:
|
||||
// 1byte == char <= short <= int <= long <= long long
|
||||
|
||||
|
|
@ -68,5 +129,43 @@ int main(int, char**) {
|
|||
|
||||
test_big();
|
||||
|
||||
#if TEST_HAS_BITINT
|
||||
// Non-byte-aligned _BitInt(N) has uninitialized padding bits; passing such a
|
||||
// value to std::abs trips a false-positive MSan report (#204217). Gate those
|
||||
// widths out under MSan; byte-aligned widths still run.
|
||||
test_signed_bitint<8>();
|
||||
test_signed_bitint<16>();
|
||||
test_signed_bitint<32>();
|
||||
test_signed_bitint<64>();
|
||||
# if !TEST_HAS_FEATURE(memory_sanitizer)
|
||||
test_signed_bitint<7>();
|
||||
test_signed_bitint<33>();
|
||||
test_signed_bitint<63>();
|
||||
test_signed_bitint<65>();
|
||||
# endif
|
||||
# if __BITINT_MAXWIDTH__ >= 128
|
||||
test_signed_bitint<128>();
|
||||
# endif
|
||||
# if __BITINT_MAXWIDTH__ >= 256
|
||||
# if !TEST_HAS_FEATURE(memory_sanitizer)
|
||||
test_signed_bitint<129>();
|
||||
# endif
|
||||
test_signed_bitint<256>();
|
||||
|
||||
// Large value: |-2^200| == 2^200.
|
||||
signed _BitInt(256) v = -(static_cast<signed _BitInt(256)>(1) << 200);
|
||||
signed _BitInt(256) expected = static_cast<signed _BitInt(256)>(1) << 200;
|
||||
assert(std::abs(v) == expected);
|
||||
# endif
|
||||
# if __BITINT_MAXWIDTH__ >= 1024
|
||||
test_signed_bitint<512>();
|
||||
test_signed_bitint<1024>();
|
||||
# endif
|
||||
#endif // TEST_HAS_BITINT
|
||||
|
||||
#ifndef TEST_HAS_NO_INT128
|
||||
test_int128();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue