Skip to content

Commit 3bdfca5

Browse files
[libc++] Workaround for a bug of overloads in MS UCRT's <math.h> (#149234)
MS UCRT seems confused on the status of LWG1327, and still provides pre-LWG1327 overload set the related math functions, which can't handle integer types as required. It is probably that UCRT won't fixed this in a near future, per https://developercommunity.visualstudio.com/t/10294165. Before C++20, libc++ worked around this bug by relying on `-fdelayed-template-parsing`. However, this non-conforming option is off by default since C++20. I think we should use `requires` instead. --------- Co-authored-by: Louis Dionne <[email protected]>
1 parent d1b363e commit 3bdfca5

File tree

5 files changed

+95
-15
lines changed

5 files changed

+95
-15
lines changed

libcxx/include/__math/traits.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,82 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
189189
return __builtin_isunordered((type)__x, (type)__y);
190190
}
191191

192+
// MS UCRT incorrectly defines some functions in a way not working with integer types. Until C++20, this was worked
193+
// around by -fdelayed-template-parsing. Since C++20, we can use standard feature "requires" instead.
194+
195+
// TODO: Remove the workaround once UCRT fixes these functions. Note that this doesn't seem planned as of 2025-07 per
196+
// https://developercommunity.visualstudio.com/t/10294165.
197+
198+
#if defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
199+
namespace __ucrt {
200+
template <class _A1>
201+
requires is_integral_v<_A1>
202+
[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isfinite(_A1) noexcept {
203+
return true;
204+
}
205+
206+
template <class _A1>
207+
requires is_integral_v<_A1>
208+
[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(_A1) noexcept {
209+
return false;
210+
}
211+
212+
template <class _A1>
213+
requires is_integral_v<_A1>
214+
[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(_A1) noexcept {
215+
return false;
216+
}
217+
218+
template <class _A1>
219+
requires is_integral_v<_A1>
220+
[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(_A1 __x) noexcept {
221+
return __x != 0;
222+
}
223+
224+
template <class _A1, class _A2>
225+
requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
226+
[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
227+
using type = __promote_t<_A1, _A2>;
228+
return __builtin_isgreater((type)__x, (type)__y);
229+
}
230+
231+
template <class _A1, class _A2>
232+
requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
233+
[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
234+
using type = __promote_t<_A1, _A2>;
235+
return __builtin_isgreaterequal((type)__x, (type)__y);
236+
}
237+
238+
template <class _A1, class _A2>
239+
requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
240+
[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
241+
using type = __promote_t<_A1, _A2>;
242+
return __builtin_isless((type)__x, (type)__y);
243+
}
244+
245+
template <class _A1, class _A2>
246+
requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
247+
[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
248+
using type = __promote_t<_A1, _A2>;
249+
return __builtin_islessequal((type)__x, (type)__y);
250+
}
251+
252+
template <class _A1, class _A2>
253+
requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
254+
[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
255+
using type = __promote_t<_A1, _A2>;
256+
return __builtin_islessgreater((type)__x, (type)__y);
257+
}
258+
259+
template <class _A1, class _A2>
260+
requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
261+
[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
262+
using type = __promote_t<_A1, _A2>;
263+
return __builtin_isunordered((type)__x, (type)__y);
264+
}
265+
} // namespace __ucrt
266+
#endif
267+
192268
} // namespace __math
193269

194270
_LIBCPP_END_NAMESPACE_STD

libcxx/include/math.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,25 @@ using std::__math::isnormal;
429429
using std::__math::isunordered;
430430
# endif // _LIBCPP_MSVCRT
431431

432+
# if defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
433+
// MS UCRT incorrectly defines some functions in a way not working with integer types. Until C++20, this was worked
434+
// around by -fdelayed-template-parsing. Since C++20, we can use standard feature "requires" instead.
435+
436+
// TODO: Remove the workaround once UCRT fixes these functions. Note that this doesn't seem planned as of 2025-07 per
437+
// https://developercommunity.visualstudio.com/t/10294165.
438+
439+
using std::__math::__ucrt::isfinite;
440+
using std::__math::__ucrt::isgreater;
441+
using std::__math::__ucrt::isgreaterequal;
442+
using std::__math::__ucrt::isinf;
443+
using std::__math::__ucrt::isless;
444+
using std::__math::__ucrt::islessequal;
445+
using std::__math::__ucrt::islessgreater;
446+
using std::__math::__ucrt::isnan;
447+
using std::__math::__ucrt::isnormal;
448+
using std::__math::__ucrt::isunordered;
449+
# endif // defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
450+
432451
// We have to provide double overloads for <math.h> to work on platforms that don't provide the full set of math
433452
// functions. To make the overload set work with multiple functions that take the same arguments, we make our overloads
434453
// templates. Functions are preferred over function templates during overload resolution, which means that our overload

libcxx/test/libcxx/fuzzing/random.pass.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
// This doesn't work on Windows because in the MSVC UCRT headers the math.h is
10-
// actually intended to implement the full C++ spec requirements. For details
11-
// see https://github.com/llvm/llvm-project/issues/70225#issuecomment-1992528828
12-
// XFAIL: msvc
13-
149
// UNSUPPORTED: c++03, c++11
1510

1611
#include <cassert>

libcxx/test/std/depr/depr.c.headers/math_h.pass.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
// This doesn't work on Windows because in the MSVC UCRT headers the math.h is
10-
// actually intended to implement the full C++ spec requirements. For details
11-
// see https://github.com/llvm/llvm-project/issues/70225#issuecomment-1992528828
12-
// XFAIL: msvc
13-
149
// <math.h>
1510

1611
// GCC warns about signbit comparing `bool_v < 0`, which we're testing

libcxx/test/std/numerics/c.math/cmath.pass.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
// This doesn't work on Windows because in the MSVC UCRT headers the math.h is
10-
// actually intended to implement the full C++ spec requirements. For details
11-
// see https://github.com/llvm/llvm-project/issues/70225#issuecomment-1992528828
12-
// XFAIL: msvc
13-
149
// <cmath>
1510

1611
#include <cmath>

0 commit comments

Comments
 (0)