mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 12:16:07 +00:00
[libc++][math] Fix acceptance of convertible types in std::isnan()
and std::isinf()
(#98952)
Following up on https://github.com/llvm/llvm-project/pull/98841. Changes: - Properly test convertible types for `std::isnan()` and `std::inf()` - Tighten conditional in `cmath.pass.cpp` (Find insights on `_LIBCPP_PREFERRED_OVERLOAD` below) - Tighten preprocessor guard in `traits.h` Insights into why `_LIBCPP_PREFERRED_OVERLOAD` is needed: (i) When libc++ is layered on top of glibc on Linux, glibc's `math.h` is included. When compiling with `-std=c++03`, this header brings the function declaration of `isinf(double)` [1] and `isnan(double)` [2] into scope. This differs from the C99 Standard as only the macros `#define isnan(arg)` and `#define isinf(arg)` are expected. Therefore, libc++ needs to respect the presense of the `double` overload and cannot redefine it as it will conflict with the declaration already in scope. For `-std=c++11` and beyond this issue is fixed, as glibc guards both the `isinf` and `isnan` by preprocessor macros. (ii) When libc++ is layered on top of Bionic's libc, `math.h` exposes a function prototype for `isinf(double)` with return type `int`. This function prototype in Bionic's libc is not guarded by any preprocessor macros [3]. `_LIBCPP_PREFERRED_OVERLOAD` specifies that a given overload is a better match than an otherwise equally good function declaration. This is implemented in modern versions of Clang via `__attribute__((__enable_if__))`, and not elsewhere. See [4] for details. We use `_LIBCPP_PREFERRED_OVERLOAD` to define overloads in the global namespace that displace the overloads provided by the C libraries mentioned above. [1]:fe94080875/math/bits/mathcalls.h (L185-L194)
[2]:fe94080875/math/bits/mathcalls.h (L222-L231)
[3]: https://cs.android.com/android/platform/superproject/+/master:bionic/libc/include/math.h;l=322-323;drc=master?hl=fr-BE%22https:%2F%2Fsupport.google.com%2Fmerchants%2Fanswer%2F188494%5C%22%22https:%2F%2Fsupport.google.com%2Fmerchants%2Fanswer%2F188494%5C%22 [4]:5fd17ab1b0
This commit is contained in:
parent
a07aba5d44
commit
2fe59d5259
@ -79,20 +79,22 @@ _LIBCPP_NODISCARD _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _LIBCPP_PREFERRED_OVERLOAD
|
|
||||||
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(float __x) _NOEXCEPT {
|
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(float __x) _NOEXCEPT {
|
||||||
return __builtin_isinf(__x);
|
return __builtin_isinf(__x);
|
||||||
}
|
}
|
||||||
|
|
||||||
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD bool
|
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
|
||||||
isinf(double __x) _NOEXCEPT {
|
#ifdef _LIBCPP_PREFERRED_OVERLOAD
|
||||||
|
_LIBCPP_PREFERRED_OVERLOAD
|
||||||
|
#endif
|
||||||
|
bool
|
||||||
|
isinf(double __x) _NOEXCEPT {
|
||||||
return __builtin_isinf(__x);
|
return __builtin_isinf(__x);
|
||||||
}
|
}
|
||||||
|
|
||||||
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
|
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
|
||||||
return __builtin_isinf(__x);
|
return __builtin_isinf(__x);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// isnan
|
// isnan
|
||||||
|
|
||||||
@ -106,20 +108,22 @@ _LIBCPP_NODISCARD _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _LIBCPP_PREFERRED_OVERLOAD
|
|
||||||
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(float __x) _NOEXCEPT {
|
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(float __x) _NOEXCEPT {
|
||||||
return __builtin_isnan(__x);
|
return __builtin_isnan(__x);
|
||||||
}
|
}
|
||||||
|
|
||||||
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD bool
|
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
|
||||||
isnan(double __x) _NOEXCEPT {
|
#ifdef _LIBCPP_PREFERRED_OVERLOAD
|
||||||
|
_LIBCPP_PREFERRED_OVERLOAD
|
||||||
|
#endif
|
||||||
|
bool
|
||||||
|
isnan(double __x) _NOEXCEPT {
|
||||||
return __builtin_isnan(__x);
|
return __builtin_isnan(__x);
|
||||||
}
|
}
|
||||||
|
|
||||||
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
|
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
|
||||||
return __builtin_isnan(__x);
|
return __builtin_isnan(__x);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// isnormal
|
// isnormal
|
||||||
|
|
||||||
|
@ -708,15 +708,16 @@ void test_isinf()
|
|||||||
static_assert((std::is_same<decltype(std::isinf((float)0)), bool>::value), "");
|
static_assert((std::is_same<decltype(std::isinf((float)0)), bool>::value), "");
|
||||||
|
|
||||||
typedef decltype(std::isinf((double)0)) DoubleRetType;
|
typedef decltype(std::isinf((double)0)) DoubleRetType;
|
||||||
#if !defined(__linux__) || defined(__clang__)
|
#if defined(__GLIBC__) && TEST_STD_VER == 03 && defined(TEST_COMPILER_CLANG)
|
||||||
static_assert((std::is_same<DoubleRetType, bool>::value), "");
|
|
||||||
#else
|
|
||||||
// GLIBC < 2.23 defines 'isinf(double)' with a return type of 'int' in
|
// GLIBC < 2.23 defines 'isinf(double)' with a return type of 'int' in
|
||||||
// all C++ dialects. The test should tolerate this when libc++ can't work
|
// all C++ dialects. The test should tolerate this when libc++ can't work
|
||||||
// around it.
|
// around it via `_LIBCPP_PREFERRED_OVERLOAD`, which is only available
|
||||||
|
// in modern versions of Clang, and not elsewhere.
|
||||||
// See: https://sourceware.org/bugzilla/show_bug.cgi?id=19439
|
// See: https://sourceware.org/bugzilla/show_bug.cgi?id=19439
|
||||||
static_assert((std::is_same<DoubleRetType, bool>::value
|
static_assert((std::is_same<DoubleRetType, bool>::value
|
||||||
|| std::is_same<DoubleRetType, int>::value), "");
|
|| std::is_same<DoubleRetType, int>::value), "");
|
||||||
|
#else
|
||||||
|
static_assert((std::is_same<DoubleRetType, bool>::value), "");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static_assert((std::is_same<decltype(std::isinf(0)), bool>::value), "");
|
static_assert((std::is_same<decltype(std::isinf(0)), bool>::value), "");
|
||||||
@ -794,15 +795,16 @@ void test_isnan()
|
|||||||
static_assert((std::is_same<decltype(std::isnan((float)0)), bool>::value), "");
|
static_assert((std::is_same<decltype(std::isnan((float)0)), bool>::value), "");
|
||||||
|
|
||||||
typedef decltype(std::isnan((double)0)) DoubleRetType;
|
typedef decltype(std::isnan((double)0)) DoubleRetType;
|
||||||
#if !defined(__linux__) || defined(__clang__)
|
#if defined(__GLIBC__) && TEST_STD_VER == 03 && defined(TEST_COMPILER_CLANG)
|
||||||
static_assert((std::is_same<DoubleRetType, bool>::value), "");
|
// GLIBC < 2.23 defines 'isnan(double)' with a return type of 'int' in
|
||||||
#else
|
|
||||||
// GLIBC < 2.23 defines 'isinf(double)' with a return type of 'int' in
|
|
||||||
// all C++ dialects. The test should tolerate this when libc++ can't work
|
// all C++ dialects. The test should tolerate this when libc++ can't work
|
||||||
// around it.
|
// around it via `_LIBCPP_PREFERRED_OVERLOAD`, which is only available
|
||||||
|
// in modern versions of Clang, and not elsewhere.
|
||||||
// See: https://sourceware.org/bugzilla/show_bug.cgi?id=19439
|
// See: https://sourceware.org/bugzilla/show_bug.cgi?id=19439
|
||||||
static_assert((std::is_same<DoubleRetType, bool>::value
|
static_assert((std::is_same<DoubleRetType, bool>::value
|
||||||
|| std::is_same<DoubleRetType, int>::value), "");
|
|| std::is_same<DoubleRetType, int>::value), "");
|
||||||
|
#else
|
||||||
|
static_assert((std::is_same<DoubleRetType, bool>::value), "");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static_assert((std::is_same<decltype(std::isnan(0)), bool>::value), "");
|
static_assert((std::is_same<decltype(std::isnan(0)), bool>::value), "");
|
||||||
|
@ -62,9 +62,21 @@ struct TestInt {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ConvertibleTo {
|
||||||
|
operator T() const { return T(); }
|
||||||
|
};
|
||||||
|
|
||||||
int main(int, char**) {
|
int main(int, char**) {
|
||||||
types::for_each(types::floating_point_types(), TestFloat());
|
types::for_each(types::floating_point_types(), TestFloat());
|
||||||
types::for_each(types::integral_types(), TestInt());
|
types::for_each(types::integral_types(), TestInt());
|
||||||
|
|
||||||
|
// Make sure we can call `std::isinf` with convertible types
|
||||||
|
{
|
||||||
|
assert(!std::isinf(ConvertibleTo<float>()));
|
||||||
|
assert(!std::isinf(ConvertibleTo<double>()));
|
||||||
|
assert(!std::isinf(ConvertibleTo<long double>()));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -62,9 +62,21 @@ struct TestInt {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ConvertibleTo {
|
||||||
|
operator T() const { return T(); }
|
||||||
|
};
|
||||||
|
|
||||||
int main(int, char**) {
|
int main(int, char**) {
|
||||||
types::for_each(types::floating_point_types(), TestFloat());
|
types::for_each(types::floating_point_types(), TestFloat());
|
||||||
types::for_each(types::integral_types(), TestInt());
|
types::for_each(types::integral_types(), TestInt());
|
||||||
|
|
||||||
|
// Make sure we can call `std::isnan` with convertible types
|
||||||
|
{
|
||||||
|
assert(!std::isnan(ConvertibleTo<float>()));
|
||||||
|
assert(!std::isnan(ConvertibleTo<double>()));
|
||||||
|
assert(!std::isnan(ConvertibleTo<long double>()));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user