Clang-cl: Adding /std:c++23preview

In VS 2022 17.13, MSVC is adding a /std:c++23preview option to indicate C++23-in-progress, with /std:c++latest changing to indicate C++26-in-progress. (This is because MSVC’s STL will be finishing the C++23 library-only features soon and will need to begin work on C++26 library-only features, even though MSVC’s compiler front-end C1XX still has many C++23 core language features to implement. In the C++20 era, the library and compiler reached completeness around the same time, which is why we need to alter the design now.) The intention is that when both MSVC’s STL and C1XX are C++23-complete, and we’re comfortable with locking down the ABI, we’ll add the final /std:c++23 option, and then deprecate /std:c++23preview (and maybe remove it eventually).

As part of this work, we’re changing the values used for __cplusplus (when in /Zc:__cplusplus mode, which is still not the default) and _MSVC_LANG. For C++23 we’re going to use the final Standard value 202302L. For C++26 we’re going to use the placeholder value 202600L.

We’d like clang-cl to begin understanding /std:c++23preview. (I suppose you could accept /std:c++23 as a synonym immediately, that’s up to you. cl.exe won’t be accepting that yet.) With how we’re changing the STL’s logic to detect the Standard mode (implemented in VCRuntime), it would be sufficient to make /std:c++23preview a synonym for passing -std=c++23 to underlying Clang, but it would be better to also update the __cplusplus and _MSVC_LANG values accordingly.

Here’s a self-contained test case:

C:\Temp>type meow.cpp
#include <cstdio>
using namespace std;

// Reduced from VCRuntime's logic. We use the larger of _MSVC_LANG and __cplusplus:
#if defined(_MSVC_LANG) && _MSVC_LANG > __cplusplus
#define STL_LANG _MSVC_LANG
#else
#define STL_LANG __cplusplus
#endif

#if STL_LANG > 201402L
#define HAS_CXX17 1
#else
#define HAS_CXX17 0
#endif

#if STL_LANG > 201703L
#define HAS_CXX20 1
#else
#define HAS_CXX20 0
#endif

#if STL_LANG > 202002L
#define HAS_CXX23 1
#else
#define HAS_CXX23 0
#endif

#if STL_LANG > 202302L
#define HAS_CXX26 1
#else
#define HAS_CXX26 0
#endif

int main() {
#ifdef __clang__
    printf("Clang");
#else
    printf("C1XX");
#endif

    printf("; __cplusplus: %ld; _MSVC_LANG: %ld; ", __cplusplus, _MSVC_LANG);

#if HAS_CXX26
    printf("detected C++26\n");
#elif HAS_CXX23
    printf("detected C++23\n");
#elif HAS_CXX20
    printf("detected C++20\n");
#elif HAS_CXX17
    printf("detected C++17\n");
#else
    printf("detected C++14\n");
#endif
}

When built with my development version of MSVC where I’ve added the new option:

C:\Temp>cl /EHsc /nologo /W4 /std:c++14 meow.cpp && meow
meow.cpp
C1XX; __cplusplus: 199711; _MSVC_LANG: 201402; detected C++14

C:\Temp>cl /EHsc /nologo /W4 /std:c++17 meow.cpp && meow
meow.cpp
C1XX; __cplusplus: 199711; _MSVC_LANG: 201703; detected C++17

C:\Temp>cl /EHsc /nologo /W4 /std:c++20 meow.cpp && meow
meow.cpp
C1XX; __cplusplus: 199711; _MSVC_LANG: 202002; detected C++20

C:\Temp>cl /EHsc /nologo /W4 /std:c++23preview meow.cpp && meow
meow.cpp
C1XX; __cplusplus: 199711; _MSVC_LANG: 202302; detected C++23

C:\Temp>cl /EHsc /nologo /W4 /std:c++latest meow.cpp && meow
meow.cpp
C1XX; __cplusplus: 199711; _MSVC_LANG: 202600; detected C++26

(If the conforming /Zc:__cplusplus mode were active, its value would match _MSVC_LANG in every case.)

Here is Clang 18’s behavior, showing that -Xclang -std=c++23 would be sufficient, and that -Xclang -std=c++2c behaves synonymously with /std:c++latest:

C:\Temp>clang-cl /EHsc /nologo /W4 /std:c++14 meow.cpp && meow
Clang; __cplusplus: 201402; _MSVC_LANG: 201402; detected C++14

C:\Temp>clang-cl /EHsc /nologo /W4 /std:c++17 meow.cpp && meow
Clang; __cplusplus: 201703; _MSVC_LANG: 201703; detected C++17

C:\Temp>clang-cl /EHsc /nologo /W4 /std:c++20 meow.cpp && meow
Clang; __cplusplus: 202002; _MSVC_LANG: 202002; detected C++20

C:\Temp>clang-cl /EHsc /nologo /W4 -Xclang -std=c++23 meow.cpp && meow
Clang; __cplusplus: 202302; _MSVC_LANG: 202004; detected C++23

C:\Temp>clang-cl /EHsc /nologo /W4 -Xclang -std=c++2c meow.cpp && meow
Clang; __cplusplus: 202400; _MSVC_LANG: 202004; detected C++26

C:\Temp>clang-cl /EHsc /nologo /W4 /std:c++latest meow.cpp && meow
Clang; __cplusplus: 202400; _MSVC_LANG: 202004; detected C++26
3 Likes

Hi Stephan!

We appreciate the bug report/reaching across the aisle here. I think that we would be interested in matching MSVC’s behavior here if at all possible, though Aaron is likely the decision maker here.

One way to get this done (if Aaron is OK with it), is to file this information as a bug in our bug tracker, plus add a ‘good first bug’ tag (which it IS, but gets quite some additional attention from newbies).

2 Likes

This sounds good to me - it’s better if we use the same flags across cl.exe and clang-cl.exe.

Cc @hansw2000 and @rnk as well.

Thanks for the heads up!

Taking a stab at it here: [clang-cl]: Add /std:c++23preview and update _MSVC_LANG for C++23 by zmodem · Pull Request #112378 · llvm/llvm-project · GitHub

3 Likes

Agreed, matching the driver makes perfect sense.

Thank you for letting us know about this! I agree that clang-cl should match the MSVC behavior.

Thank you for tackling this Hans!