Skip to content

Bug: <experimental/coroutine> will be deprecated in MSVC Build Tools 14.51 #1520

@StephanTLavavej

Description

@StephanTLavavej

Version

master

Summary

I work on the Visual C++ team, where we regularly build popular open-source projects, including cppwinrt, with development versions of the MSVC Build Tools in order to find and fix regressions before they can ship and cause trouble for you. This also allows us to provide advance notice of breaking changes, which is the case here.

In the MSVC Build Tools 14.50 shipping in Visual Studio 2026 18.0, the /await compiler option is officially being deprecated. This is a command-line deprecation warning that does not break builds (even under /WX):

C:\Temp>type meow.cpp
int main() {}

C:\Temp>cl /EHsc /nologo /W4 /await meow.cpp
cl : Command line warning D9047 : option 'await' has been deprecated and will be removed in a future release.
    Standard C++ coroutines are available by default in C++20 or later, or use '/await:strict' in earlier language modes.
meow.cpp

In the MSVC Build Tools 14.51 shipping in some future update to Visual Studio 2026, I am additionally deprecating the <experimental/coroutine>, <experimental/resumable>, and <experimental/generator> headers, as the first two have been superseded by C++20 <coroutine> and the third has been superseded by C++23 <generator>. This is a "hard deprecation" implemented as a static_assert. It can be acknowledged and silenced with an escape hatch macro, but future removal is coming. (PR: microsoft/STL#5804 )

We've found that cppwinrt conditionally includes <experimental/coroutine> in an unusual way. It begins by detecting support for Standard coroutines and prefers to include <coroutine> (good), but otherwise falls back to including <experimental/coroutine>. Interestingly, this is guarded by __has_include(<experimental/coroutine>) (which is always true, at least until I physically remove the header in the future), but not by detecting the /await compiler option (which is indicated by the _RESUMABLE_FUNCTIONS_SUPPORTED predefined macro):

#ifdef __cpp_lib_coroutine
#include <coroutine>
namespace winrt::impl
{
template <typename T = void>
using coroutine_handle = std::coroutine_handle<T>;
using suspend_always = std::suspend_always;
using suspend_never = std::suspend_never;
}
#elif __has_include(<experimental/coroutine>)
#include <experimental/coroutine>
namespace winrt::impl
{
template <typename T = void>
using coroutine_handle = std::experimental::coroutine_handle<T>;
using suspend_always = std::experimental::suspend_always;
using suspend_never = std::experimental::suspend_never;
}
#else
#error C++/WinRT requires coroutine support, which is currently missing. Try enabling C++20 in your compiler.
#endif

This has affected several projects in our Real World Code test suite (MuseScore which uses cppwinrt, Qgroundcontrol which uses qtspeech which uses cppwinrt, Qt6 which uses cppwinrt). Interestingly, these projects don't appear to be compiling with deprecated /await - they are simply dragging in cppwinrt, which drags in <experimental/coroutine>, which now triggers my hard deprecation error. (I believe that previously, the experimental coroutine machinery wouldn't actually have been usable without /await, but nothing was trying to use it.)

I am not exactly sure how cppwinrt should change. I think there are a few options:

Option 1: Require Standard coroutines

You could simply require <coroutine>, drop the codepath for <experimental/coroutine>, and adjust your error message that currently says "Try enabling C++20 in your compiler." to additionally mention that /await:strict can be used to opt-in to Standard coroutines even for C++14/17 mode (this is less intrusive for projects that have difficulty quickly migrating to C++20).

Option 2: Detect deprecated /await

Instead of __has_include(<experimental/coroutine>), check for _RESUMABLE_FUNCTIONS_SUPPORTED. This would block "plain vanilla" usage of cppwinrt (i.e. usage in C++14/17 mode with neither /await nor /await:strict), forcing users to choose either the Standard or the deprecated path. Again it would be good to adjust the error message to mention /await:strict.

Option 3: Detect deprecated /await, otherwise don't error

Again check for _RESUMABLE_FUNCTIONS_SUPPORTED, but instead of emitting a compiler error for users in plain vanilla C++14/17 mode, simply don't include anything. It appears that several projects are managing minimal usage of cppwinrt without actually needing coroutines, as indicated by how they were compiling in plain vanilla C++14/17 mode, dragging in cppwinrt, which included <experimental/coroutine> (before my hard deprecation error), and then did nothing with it that actually needed coroutine support. I am not exactly sure if other code would need to be adjusted, but this is potentially the least invasive option for third-party projects that could allow them to continue compiling without needing to add /await:strict (or migrate to C++20).

Macro detection example

For completeness, here is how MSVC's macros work:

C:\Temp>type meow.cpp
#include <cstdio>

int main() {
#ifdef _RESUMABLE_FUNCTIONS_SUPPORTED
    std::puts("Detected the deprecated /await compiler option.");
#endif

#ifdef __cpp_impl_coroutine
#if _MSVC_LANG > 201703L
    std::puts("Detected support for standard coroutines, available in C++20.");
#else
    std::puts("Detected support for standard coroutines, available with /await:strict in C++14/17.");
#endif
#endif

#if !defined(_RESUMABLE_FUNCTIONS_SUPPORTED) && !defined(__cpp_impl_coroutine)
    std::puts("Detected nothing.");
#endif
}
C:\Temp>(cl /EHsc /nologo /W4 /std:c++14 meow.cpp > NUL) && meow
Detected nothing.

C:\Temp>(cl /EHsc /nologo /W4 /std:c++14 /await meow.cpp > NUL) && meow
cl : Command line warning D9047 : option 'await' has been deprecated and will be removed in a future release.
    Standard C++ coroutines are available by default in C++20 or later, or use '/await:strict' in earlier language modes.
Detected the deprecated /await compiler option.

C:\Temp>(cl /EHsc /nologo /W4 /std:c++14 /await:strict meow.cpp > NUL) && meow
Detected support for standard coroutines, available with /await:strict in C++14/17.

C:\Temp>(cl /EHsc /nologo /W4 /std:c++20 meow.cpp > NUL) && meow
Detected support for standard coroutines, available in C++20.

C:\Temp>(cl /EHsc /nologo /W4 /std:c++20 /await meow.cpp > NUL) && meow
cl : Command line warning D9047 : option 'await' has been deprecated and will be removed in a future release.
    Standard C++ coroutines are available by default in C++20 or later, or use '/await:strict' in earlier language modes.
Detected the deprecated /await compiler option.

C:\Temp>(cl /EHsc /nologo /W4 /std:c++20 /await:strict meow.cpp > NUL) && meow
Detected support for standard coroutines, available in C++20.

(Here I'm using __cpp_impl_coroutine to detect compiler support for Standard coroutines, but it's fine and probably better for cppwinrt to use our library macro __cpp_lib_coroutine.)

Reproducible example

Expected behavior

No response

Actual behavior

No response

Additional comments

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions