llvm / llvm/llvm-project

[libcxx] std::isfinite, std::isinf, std::isnan and std::isnormal are not constexpr on Windows with C++23

Open
#214,358 1 comment 0 reactions 0 assignees View on GitHub
c++23 libc++ platform:windows
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

- [Summary](#summary)
- [Reproducing](#reproducing)
- [Cause](#cause)
- [Why it was never caught](#why-it-was-never-caught)

## Summary

P0533R9 makes the floating-point classification traits usable in constant
expressions in C++23. With libc++ they are, on Linux and macOS. On Windows they
are not — `` forwards them to the UCRT's non-constexpr overloads, and
`-std=c++23` has no bearing on that.

```c++
#include
static_assert(std::isfinite(1.0f));
```

```
error: static assertion expression is not an integral constant expression
2 | static_assert(std::isfinite(1.0f));
| ^~~~~~~~~~~~~~~~~~~
note: non-constexpr function 'isfinite' cannot be used in a constant expression
ucrt\corecrt_math.h(435,32): note: declared here
435 | _Check_return_ inline bool isfinite(_In_ _Ty _X) throw()
| ^
```

Same for `isinf`, `isnan` and `isnormal`.

## Reproducing

**Reproduced with:** clang trunk `23.0.0git` (`53d18800eda3`), llvm-project at
`5270be7020cb`, Windows SDK 10.0.28000.0, `/std:c++23preview`,
`x86_64-pc-windows-msvc`.

repro.cpp (click to expand)

```c++
#include
#include

#if __cplusplus < 202302L
# error "This repro requires C++23 (/std:c++23preview, /std:c++latest or -std=c++23)."
#endif

// ---------------------------------------------------------------------------
// The bug. P0533R9 makes all of these constexpr in C++23.
// ---------------------------------------------------------------------------

static_assert(std::isfinite(1.0f));
static_assert(std::isfinite(1.0));
static_assert(std::isfinite(1.0L));
static_assert(!std::isfinite(std::numeric_limits::infinity()));

static_assert(!std::isinf(1.0f));
static_assert(!std::isinf(1.0));
static_assert(!std::isinf(1.0L));
static_assert(std::isinf(std::numeric_limits::infinity()));

static_assert(!std::isnan(1.0f));
static_assert(!std::isnan(1.0));
static_assert(!std::isnan(1.0L));
static_assert(std::isnan(std::numeric_limits::quiet_NaN()));

static_assert(std::isnormal(1.0f));
static_assert(std::isnormal(1.0));
static_assert(std::isnormal(1.0L));
static_assert(!std::isnormal(std::numeric_limits::denorm_min()));

// Same through the global namespace, which is what exposes.
static_assert(::isfinite(1.0f));
static_assert(!::isinf(1.0));
static_assert(!::isnan(1.0L));
static_assert(::isnormal(1.0));

int main() { return 0; }
```

```bat
clang-cl /c /nologo /TP /std:c++23preview -Xclang -nostdinc++ ^
-I\include\c++\v1 repro.cpp
```

Note that Compiler Explorer **cannot** run this one: there is no libc++-on-Windows
configuration. No C++ compiler entry exposes a `stdlib` override; under clang-cl,
`-stdlib=libc++` is ignored with `warning: unknown argument ignored in clang-cl`
and `_LIBCPP_VERSION` stays undefined. Cross-compiling from CE's Linux clangs
with `--target=x86_64-pc-windows-msvc` fails with `'cmath' file not found`, since
those hosts have no UCRT. This was requested in
[compiler-explorer#1967](https://github.com/compiler-explorer/compiler-explorer/issues/1967)
— the clang-cl half shipped, the libc++ half did not.

## Cause

The UCRT declares these traits in the **global** namespace as non-constexpr,
unconstrained function templates (`corecrt_math.h`, Windows Kits 10.0.28000.0):

```c++
extern "C++" {
template
_Check_return_ inline bool isfinite(_In_ _Ty _X) throw() { return fpclassify(_X) <= 0; }
// ... same shape for isinf, isnan, isnormal
}
```

libc++'s `` deliberately does **not** re-export its own overloads there:

```c++
// libcxx/include/math.h
// The MSVC runtime already provides these functions as templates
#ifndef _LIBCPP_MSVCRT
using std::__math::isfinite;
// ...
#endif
```

so the only floating-point candidates in scope are the UCRT's. `` then
does `using ::isfinite _LIBCPP_USING_IF_EXISTS;`, which makes `std::isfinite`
resolve to the non-constexpr UCRT template.

The only thing libc++ currently adds on Windows is a set of `requires`-constrained
**integral** overloads in `std::__math::__ucrt`, working around a separate UCRT
bug: its `fpclassify` overload set is ambiguous for integers, so `isfinite(1)`
fails to compile inside the UCRT's own body. See
[VS developer community #10294165](https://developercommunity.visualstudio.com/t/10294165).

## Why it was never caught

Every test that would have caught this is `UNSUPPORTED: windows`:

- `libcxx/test/std/numerics/c.math/{isfinite,isinf,isnan,isnormal,signbit}.pass.cpp`
— "We don't control the implementation on windows"
- `libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp`
— "We don't control the implementation of these functions on windows"

Those exclusions are justified for other reasons (see *Out of scope* below), but
they took the constexpr-ness of these four traits down with them.

Contributor guide

Open the contributing guide

Research direction

Start with libcxx/include/math.h and cmath.h, then reproduce the C++23 static assertions with clang-cl on Windows using the provided repro.cpp. Review the Windows exclusions in libcxx/test/std/numerics/c.math/{isfinite,isinf,isnan,isnormal,signbit}.pass.cpp and libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp; done means the four traits are constexpr on Windows without regressing integral handling.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers, operating-systems, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.