diff --git a/stl/inc/cmath b/stl/inc/cmath index 8bb994af15b..f4e0ae7072d 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -594,6 +594,50 @@ _STD _Common_float_type_t<_Ty1, _Ty2> remquo(_Ty1 _Left, _Ty2 _Right, int* _Pquo } } +// TRANSITION, GH-519, should be provided by UCRT +template , int> = 0> +_NODISCARD _Check_return_ _CONSTEXPR23 int fpclassify(_In_ const _Ty _Ix) noexcept /* strengthened */ { + return _Ix == 0 ? FP_ZERO : FP_NORMAL; +} + +// TRANSITION, GH-519, should be provided by UCRT +template , int> = 0> +_NODISCARD _Check_return_ _CONSTEXPR23 bool signbit(_In_ const _Ty _Ix) noexcept /* strengthened */ { + if constexpr (static_cast<_Ty>(-1) < _Ty{}) { + return _Ix < 0; + } else { + return false; + } +} + +// TRANSITION, GH-519, additional overloads are not templated to avoid ambiguity with the major overload in UCRT +#define _GENERIC_MATH_ISNORMAL(TYPE) \ + _NODISCARD _Check_return_ _CONSTEXPR23 bool isnormal(_In_ const TYPE _Ix) noexcept /* strengthened */ { \ + return _Ix != 0; \ + } + +_GENERIC_MATH_ISNORMAL(signed char) +_GENERIC_MATH_ISNORMAL(unsigned char) +_GENERIC_MATH_ISNORMAL(short) +_GENERIC_MATH_ISNORMAL(unsigned short) +_GENERIC_MATH_ISNORMAL(int) +_GENERIC_MATH_ISNORMAL(unsigned int) +_GENERIC_MATH_ISNORMAL(long) +_GENERIC_MATH_ISNORMAL(unsigned long) +_GENERIC_MATH_ISNORMAL(long long) +_GENERIC_MATH_ISNORMAL(unsigned long long) +_GENERIC_MATH_ISNORMAL(bool) +_GENERIC_MATH_ISNORMAL(char) +#ifdef __cpp_char8_t +_GENERIC_MATH_ISNORMAL(char8_t) +#endif // defined(__cpp_char8_t) +_GENERIC_MATH_ISNORMAL(char16_t) +_GENERIC_MATH_ISNORMAL(char32_t) +#ifdef _NATIVE_WCHAR_T_DEFINED +_GENERIC_MATH_ISNORMAL(wchar_t) +#endif // defined(_NATIVE_WCHAR_T_DEFINED) +#undef _GENERIC_MATH_ISNORMAL + #define _GENERIC_MATH1_BASE(NAME, RET, FUN) \ template , int> = 0> \ _NODISCARD RET NAME(_Ty _Left) noexcept /* strengthened */ { \ @@ -640,6 +684,42 @@ _STD _Common_float_type_t<_Ty1, _Ty2> remquo(_Ty1 _Left, _Ty2 _Right, int* _Pquo #define _GENERIC_MATH2I(FUN, CLANG_INTRIN, MSVC_INTRIN) _GENERIC_MATH2_BASE(FUN, _CSTD FUN) #endif // ^^^ intrinsics unavailable ^^^ +// TRANSITION, GH-519, additional overloads are not templated to avoid ambiguity with the major overload in UCRT +#define _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, TYPE) \ + _NODISCARD _Check_return_ _CONSTEXPR23 bool FUN(_In_ TYPE) noexcept /* strengthened */ { \ + return RETV; \ + } + +#ifdef __cpp_char8_t +#define _GENERIC_MATH_CLASSIFY1_RETV_CHAR8_T(FUN, RETV) _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, char8_t) +#else // ^^^ defined(__cpp_char8_t) / !defined(__cpp_char8_t) vvv +#define _GENERIC_MATH_CLASSIFY1_RETV_CHAR8_T(FUN, RETV) +#endif // ^^^ !defined(__cpp_char8_t) ^^^ + +#ifdef _NATIVE_WCHAR_T_DEFINED +#define _GENERIC_MATH_CLASSIFY1_RETV_WCHAR_T(FUN, RETV) _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, wchar_t) +#else // ^^^ defined(_NATIVE_WCHAR_T_DEFINED) / !defined(_NATIVE_WCHAR_T_DEFINED) vvv +#define _GENERIC_MATH_CLASSIFY1_RETV_WCHAR_T(FUN, RETV) +#endif // ^^^ !defined(_NATIVE_WCHAR_T_DEFINED) ^^^ + +#define _GENERIC_MATH_CLASSIFY1_RETV(FUN, RETV) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, signed char) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, unsigned char) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, short) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, unsigned short) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, int) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, unsigned int) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, long) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, unsigned long) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, long long) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, unsigned long long) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, bool) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, char) \ + _GENERIC_MATH_CLASSIFY1_RETV_CHAR8_T(FUN, RETV) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, char16_t) \ + _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, char32_t) \ + _GENERIC_MATH_CLASSIFY1_RETV_WCHAR_T(FUN, RETV) + // The following order matches N4950 [cmath.syn]. _GENERIC_MATH1(acos) _GENERIC_MATH1(asin) @@ -701,7 +781,12 @@ _GENERIC_MATH2(fmax) _GENERIC_MATH2(fmin) // fma() is hand-crafted // lerp() is hand-crafted -// The "classification/comparison functions" (fpclassify(), etc.) are exempt, LWG-1327 +// fpclassify() is hand-crafted +_GENERIC_MATH_CLASSIFY1_RETV(isfinite, true) +_GENERIC_MATH_CLASSIFY1_RETV(isinf, false) +_GENERIC_MATH_CLASSIFY1_RETV(isnan, false) +// isnormal() is half-hand-crafted +// signbit() is hand-crafted #undef _GENERIC_MATH1_BASE #undef _GENERIC_MATH1R @@ -711,6 +796,10 @@ _GENERIC_MATH2(fmin) #undef _GENERIC_MATH2_BASE #undef _GENERIC_MATH2 #undef _GENERIC_MATH2I +#undef _GENERIC_MATH_CLASSIFY1_RETV_INTEGER +#undef _GENERIC_MATH_CLASSIFY1_RETV_WCHAR_T +#undef _GENERIC_MATH_CLASSIFY1_RETV_CHAR8_T +#undef _GENERIC_MATH_CLASSIFY1_RETV #undef _HAS_CMATH_INTRINSICS _END_EXTERN_CXX_WORKAROUND diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index ba26ae5bc1f..5211f2ab232 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -484,10 +484,6 @@ std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp:2 # *** STL BUGS *** -# GH-519 : signbit() misses overloads for integer types -std/depr/depr.c.headers/math_h.pass.cpp FAIL -std/numerics/c.math/cmath.pass.cpp FAIL - # GH-784 : aligned_storage has incorrect alignment defaults std/utilities/meta/meta.trans/meta.trans.other/aligned_storage.pass.cpp FAIL diff --git a/tests/std/test.lst b/tests/std/test.lst index cf8d9bf3ef1..a3ec79e1268 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -168,6 +168,7 @@ tests\GH_000431_lex_compare_family tests\GH_000431_lex_compare_memcmp_classify tests\GH_000442_random_subtract_with_carry_engine_io tests\GH_000457_system_error_message +tests\GH_000519_cmath_int_overloads tests\GH_000527_remove_allocator_void tests\GH_000545_include_compare tests\GH_000625_vector_bool_optimization diff --git a/tests/std/tests/GH_000519_cmath_int_overloads/env.lst b/tests/std/tests/GH_000519_cmath_int_overloads/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000519_cmath_int_overloads/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000519_cmath_int_overloads/test.cpp b/tests/std/tests/GH_000519_cmath_int_overloads/test.cpp new file mode 100644 index 00000000000..8954620c506 --- /dev/null +++ b/tests/std/tests/GH_000519_cmath_int_overloads/test.cpp @@ -0,0 +1,190 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +// Also test the partial implementation of P0533R9 "constexpr For And " +#if _HAS_CXX23 +#define CONSTEXPR23 constexpr +#else // ^^^ _HAS_CXX23 / !_HAS_CXX23 vvv +#define CONSTEXPR23 inline +#endif // ^^^ !_HAS_CXX23 ^^^ + +CONSTEXPR23 void test_bool_overloads() { + // test overloads in std + + assert(std::fpclassify(false) == FP_ZERO); + assert(std::fpclassify(true) == FP_NORMAL); + + assert(std::isfinite(false)); + assert(std::isfinite(true)); + + assert(!std::isinf(false)); + assert(!std::isinf(true)); + + assert(!std::isnan(false)); + assert(!std::isnan(true)); + + assert(!std::isnormal(false)); + assert(std::isnormal(true)); + + assert(!std::signbit(false)); + assert(!std::signbit(true)); + +#if _HAS_CXX23 // TRANSITION, UCRT should implement P0533R9 "constexpr For And " + if (!std::is_constant_evaluated()) +#endif // ^^^ _HAS_CXX23 ^^^ + { + assert(!std::isgreater(false, true)); + assert(!std::isgreaterequal(false, true)); + assert(std::isless(false, true)); + assert(std::islessequal(false, true)); + assert(std::islessgreater(false, true)); + assert(!std::isunordered(false, true)); + } + + // test overloads in the global namespace + + assert(::fpclassify(false) == FP_ZERO); + assert(::fpclassify(true) == FP_NORMAL); + + assert(::isfinite(false)); + assert(::isfinite(true)); + + assert(!::isinf(false)); + assert(!::isinf(true)); + + assert(!::isnan(false)); + assert(!::isnan(true)); + + assert(!::isnormal(false)); + assert(::isnormal(true)); + + assert(!::signbit(false)); + assert(!::signbit(true)); + +#if _HAS_CXX23 // TRANSITION, UCRT should implement P0533R9 "constexpr For And " + if (!std::is_constant_evaluated()) +#endif // ^^^ _HAS_CXX23 ^^^ + { + assert(!::isgreater(false, true)); + assert(!::isgreaterequal(false, true)); + assert(::isless(false, true)); + assert(::islessequal(false, true)); + assert(::islessgreater(false, true)); + assert(!::isunordered(false, true)); + } +} + +template +CONSTEXPR23 void test_other_integral_overloads() { + // test overloads in std + + assert(std::fpclassify(I{}) == FP_ZERO); + assert(std::fpclassify(static_cast(42)) == FP_NORMAL); + assert(std::fpclassify(static_cast(-42)) == FP_NORMAL); + + assert(std::isfinite(I{})); + assert(std::isfinite(static_cast(42))); + assert(std::isfinite(static_cast(-42))); + + assert(!std::isinf(I{})); + assert(!std::isinf(static_cast(42))); + assert(!std::isinf(static_cast(-42))); + + assert(!std::isnan(I{})); + assert(!std::isnan(static_cast(42))); + assert(!std::isnan(static_cast(-42))); + + assert(!std::isnormal(I{})); + assert(std::isnormal(static_cast(42))); + assert(std::isnormal(static_cast(-42))); + + assert(!std::signbit(I{})); + assert(!std::signbit(static_cast(42))); + assert(std::signbit(static_cast(-42)) == std::is_signed_v); + +#if _HAS_CXX23 // TRANSITION, UCRT should implement P0533R9 "constexpr For And " + if (!std::is_constant_evaluated()) +#endif // ^^^ _HAS_CXX23 ^^^ + { + assert(!std::isgreater(static_cast(17), static_cast(29))); + assert(!std::isgreaterequal(static_cast(17), static_cast(29))); + assert(std::isless(static_cast(17), static_cast(29))); + assert(std::islessequal(static_cast(17), static_cast(29))); + assert(std::islessgreater(static_cast(17), static_cast(29))); + assert(!std::isunordered(static_cast(17), static_cast(29))); + } + + // test overloads in the global namespace + + assert(::fpclassify(I{}) == FP_ZERO); + assert(::fpclassify(static_cast(42)) == FP_NORMAL); + assert(::fpclassify(static_cast(-42)) == FP_NORMAL); + + assert(::isfinite(I{})); + assert(::isfinite(static_cast(42))); + assert(::isfinite(static_cast(-42))); + + assert(!::isinf(I{})); + assert(!::isinf(static_cast(42))); + assert(!::isinf(static_cast(-42))); + + assert(!::isnan(I{})); + assert(!::isnan(static_cast(42))); + assert(!::isnan(static_cast(-42))); + + assert(!::isnormal(I{})); + assert(::isnormal(static_cast(42))); + assert(::isnormal(static_cast(-42))); + + assert(!::signbit(I{})); + assert(!::signbit(static_cast(42))); + assert(::signbit(static_cast(-42)) == std::is_signed_v); + +#if _HAS_CXX23 // TRANSITION, UCRT should implement P0533R9 "constexpr For And " + if (!std::is_constant_evaluated()) +#endif // ^^^ _HAS_CXX23 ^^^ + { + assert(!::isgreater(static_cast(17), static_cast(29))); + assert(!::isgreaterequal(static_cast(17), static_cast(29))); + assert(::isless(static_cast(17), static_cast(29))); + assert(::islessequal(static_cast(17), static_cast(29))); + assert(::islessgreater(static_cast(17), static_cast(29))); + assert(!::isunordered(static_cast(17), static_cast(29))); + } +} + +CONSTEXPR23 bool test_all_integral_overloads() { + test_bool_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); +#ifdef __cpp_char8_t + test_other_integral_overloads(); +#endif // defined(__cpp_char8_t) + test_other_integral_overloads(); + test_other_integral_overloads(); + test_other_integral_overloads(); + + return true; +} + +#if _HAS_CXX23 +static_assert(test_all_integral_overloads()); +#endif // _HAS_CXX23 + +int main() { + test_all_integral_overloads(); +}