[Clang] No longer advertise support for coroutines on x86 windows. (#193456)

There are a large number of long standing issues with coroutines on
x86_32 windows as discussed here

https://github.com/llvm/llvm-project/issues/59382

- #59382
- #58556
- #58543
- #56989
- #193161
- #136481

As such this patches
  - No longer defines `__cpp_impl_coroutine` on that platform
  - Warn when using coroutines on that platform

The reason to not plainly error is that it would break too much valid
valid code, ie there are people who probably use coroutines sucessfully.

And we also want to keep testing on that platform, up until the point we
were to pull support completely.

Hopefully this is all temporary and someone will feel compelled to
improve the situation enough that we can unceremoniously revert this
patch.
This commit is contained in:
Corentin Jabot 2026-04-27 12:30:03 +02:00 committed by GitHub
parent 31ea083c6b
commit 94e0fd0988
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 115 additions and 47 deletions

View file

@ -86,7 +86,10 @@ Clang Frontend Potentially Breaking Changes
libraries will need to be recompiled, or used with
(`--no-offload-new-driver`). This option will be removed in the next release.
- Clang no longer defines the ``__cpp_impl_coroutine`` feature test macro under the 32-bit x86 Microsoft ABI,
as support for coroutines on this target is incomplete.
When using coroutines on this target a warning is emmitted to indicate the lack of full support.
That warning can be disabled with ``-Wno-coroutines-unsupported-target``. (see #GH59382)
Clang Python Bindings Potentially Breaking Changes
--------------------------------------------------

View file

@ -12878,6 +12878,9 @@ def note_redefinition_include_same_file : Note<
}
let CategoryName = "Coroutines Issue" in {
def warn_coroutines_x86_windows : Warning<
"coroutines are not currently supported on the 32-bit x86 Microsoft ABI">,
InGroup<DiagGroup<"coroutines-unsupported-target">>;
def err_return_in_coroutine : Error<
"return statement not allowed in coroutine; did you mean 'co_return'?">;
def note_declared_coroutine_here : Note<

View file

@ -621,7 +621,8 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI,
/// Initialize the predefined C++ language feature test macros defined in
/// ISO/IEC JTC1/SC22/WG21 (C++) SD-6: "SG10 Feature Test Recommendations".
static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
MacroBuilder &Builder) {
MacroBuilder &Builder,
const TargetInfo &TI) {
// C++98 features.
if (LangOpts.RTTI)
Builder.defineMacro("__cpp_rtti", "199711L");
@ -714,7 +715,12 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
Builder.defineMacro("__cpp_consteval", "202211L");
Builder.defineMacro("__cpp_constexpr_dynamic_alloc", "201907L");
Builder.defineMacro("__cpp_constinit", "201907L");
Builder.defineMacro("__cpp_impl_coroutine", "201902L");
// Support for coroutines on 32-bit x86 Microsoft platforms is
// incomplete, do not advertise it.
if (!(TI.getCXXABI().isMicrosoft() && TI.getTriple().isX86_32()))
Builder.defineMacro("__cpp_impl_coroutine", "201902L");
Builder.defineMacro("__cpp_designated_initializers", "201707L");
Builder.defineMacro("__cpp_impl_three_way_comparison", "201907L");
// Intentionally to set __cpp_modules to 1.
@ -980,7 +986,7 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
Twine(TI.useSignedCharForObjCBool() ? "0" : "1"));
if (LangOpts.CPlusPlus)
InitializeCPlusPlusFeatureTestMacros(LangOpts, Builder);
InitializeCPlusPlusFeatureTestMacros(LangOpts, Builder, TI);
// darwin_constant_cfstrings controls this. This is also dependent
// on other things like the runtime I believe. This is set even for C code.

View file

@ -21,6 +21,7 @@
#include "clang/AST/IgnoreExpr.h"
#include "clang/AST/StmtCXX.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Initialization.h"
@ -693,6 +694,13 @@ bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc,
if (!checkCoroutineContext(*this, KWLoc, Keyword))
return false;
// Support for coroutines is not stable on 32 bits windows
// Warn about it.
if (Context.getTargetInfo().getCXXABI().isMicrosoft() &&
Context.getTargetInfo().getTriple().isX86_32())
Diag(KWLoc, diag::warn_coroutines_x86_windows);
auto *ScopeInfo = getCurFunction();
assert(ScopeInfo->CoroutinePromise);

View file

@ -1,4 +1,4 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.UncountedLambdaCapturesChecker -std=c++20 -verify %s
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.UncountedLambdaCapturesChecker -std=c++20 -verify %s -Wno-coroutines-unsupported-target
// expected-no-diagnostics
template<typename Arg>

View file

@ -1,6 +1,6 @@
// RUN: rm -f %t.14 %t.2a
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++14 -DCXX2A=0 -fblocks -Wall -Wno-unused -Werror %s > %t.14 2>&1
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++2a -DCXX2A=1 -fblocks -Wall -Wno-unused -Werror %s > %t.2a 2>&1
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++14 -DCXX2A=0 -fblocks -Wall -Wno-unused -Werror -Wno-coroutines-unsupported-target %s > %t.14 2>&1
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++2a -DCXX2A=1 -fblocks -Wall -Wno-unused -Werror -Wno-coroutines-unsupported-target %s > %t.2a 2>&1
// RUN: FileCheck --input-file=%t.14 -check-prefixes=CHECK,CXX14 -implicit-check-not=destructor %s
// RUN: FileCheck --input-file=%t.2a -check-prefixes=CHECK,CXX2A -implicit-check-not=destructor %s

View file

@ -1,6 +1,6 @@
// This test merely verifies that emitting the object file does not cause a
// crash when the LLVM coroutines passes are run.
// RUN: %clang_cc1 -emit-obj -std=c++2a -fsanitize=null %s -o %t.o
// RUN: %clang_cc1 -emit-obj -std=c++2a -fsanitize=null -Wno-coroutines-unsupported-target %s -o %t.o
// UNSUPPORTED: target={{.*}}-zos{{.*}}
namespace std {

View file

@ -3,7 +3,7 @@
// Vefifies that parameter copies are used in the body of the coroutine
// Verifies that parameter copies are used to construct the promise type, if that type has a matching constructor
// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s
// RUN: %clang_cc1 -std=c++20 -triple=x86_64-pc-win32 -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s --check-prefix=MSABI
// RUN: %clang_cc1 -std=c++20 -triple=x86_64-pc-win32 -emit-llvm -o - %s -disable-llvm-passes -fexceptions -Wno-coroutines-unsupported-target | FileCheck %s --check-prefix=MSABI
namespace std {
template <typename... T> struct coroutine_traits;

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++20 -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
// RUN: %clang_cc1 -std=c++20 -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes -Wno-coroutines-unsupported-target | FileCheck %s
// -triple=x86_64-unknown-linux-gnu
#include "Inputs/coroutine.h"

View file

@ -6,6 +6,11 @@
// RUN: %clang_cc1 -std=c++23 -fcxx-exceptions -verify %s
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -verify %s
// RUN: %clang_cc1 -std=c++20 -fcxx-exceptions -verify -triple i686-unknown-windows-msvc -DX86_MSVC_TARGET %s
// RUN: %clang_cc1 -std=c++23 -fcxx-exceptions -verify -triple i686-unknown-windows-msvc -DX86_MSVC_TARGET %s
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -verify -triple i686-unknown-windows-msvc -DX86_MSVC_TARGET %s
//
// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -DCONCEPTS_TS=1 -verify %s
// RUN: %clang_cc1 -std=c++14 -fno-rtti -fno-threadsafe-statics -verify %s -DNO_EXCEPTIONS -DNO_RTTI -DNO_THREADSAFE_STATICS
@ -142,8 +147,13 @@
#error "wrong value for __cpp_impl_three_way_comparison"
#endif
#if check(impl_coroutine, 0, 0, 0, 0, 201902L, 201902L, 201902L)
#error "wrong value for __cpp_impl_coroutine"
#if X86_MSVC_TARGET
# if check(impl_coroutine, 0, 0, 0, 0, 0, 0, 0)
# error "wrong value for __cpp_impl_coroutine"
# endif
#elif !(__i386__ && _WIN32) && check(impl_coroutine, 0, 0, 0, 0, 201902, 201902, 201902)
# error "wrong value for __cpp_impl_coroutine" __cpp_impl_coroutine
#endif
// init_captures checked below

View file

@ -1,8 +1,8 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: %clang_cc1 -std=c++20 %t/task.cppm -I%t -emit-reduced-module-interface -o %t/task.pcm
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/user.cpp -verify -fsyntax-only
// RUN: %clang_cc1 -std=c++20 %t/task.cppm -I%t -emit-reduced-module-interface -o %t/task.pcm -Wno-coroutines-unsupported-target
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/user.cpp -verify -fsyntax-only -Wno-coroutines-unsupported-target
//--- coroutine.h

View file

@ -1,9 +1,9 @@
// Test this without pch.
// RUN: %clang_cc1 -include %s -verify -std=c++20 %s
// RUN: %clang_cc1 -include %s -verify -std=c++20 %s -Wno-coroutines-unsupported-target
// Test with pch.
// RUN: %clang_cc1 -std=c++20 -emit-pch -o %t %s
// RUN: %clang_cc1 -include-pch %t -verify -std=c++20 %s
// RUN: %clang_cc1 -std=c++20 -emit-pch -o %t %s -Wno-coroutines-unsupported-target
// RUN: %clang_cc1 -include-pch %t -verify -std=c++20 %s -Wno-coroutines-unsupported-target
#ifndef HEADER
#define HEADER

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++20 %s -verify
// RUN: %clang_cc1 -std=c++20 %s -verify -Wno-coroutines-unsupported-target
template<typename T, typename U>
U f(T t) {

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify -Wno-coroutines-unsupported-target %s
#include "Inputs/std-coroutine.h"

View file

@ -1,5 +1,5 @@
// RUN: %clang_cc1 -std=c++20 -ast-dump -ast-dump-filter=foo %s | FileCheck %s --strict-whitespace
// RUN: %clang_cc1 -std=c++20 -triple i386-windows-pc -ast-dump -ast-dump-filter=foo %s | FileCheck %s --strict-whitespace
// RUN: %clang_cc1 -std=c++20 -ast-dump -ast-dump-filter=foo %s -Wno-coroutines-unsupported-target | FileCheck %s --strict-whitespace
// RUN: %clang_cc1 -std=c++20 -triple i386-windows-pc -ast-dump -ast-dump-filter=foo %s -Wno-coroutines-unsupported-target | FileCheck %s --strict-whitespace
namespace std {
template <typename, typename...> struct coroutine_traits;

View file

@ -1,5 +1,5 @@
// Tests that we wouldn't generate an allocation call in promise_type with (std::size_t, std::nothrow_t) in case we find promsie_type::get_return_object_on_allocation_failure;
// RUN: %clang_cc1 %s -std=c++20 %s -fsyntax-only -verify
// RUN: %clang_cc1 %s -std=c++20 -Wno-coroutines-unsupported-target %s -fsyntax-only -verify
namespace std {
template <typename... T>

View file

@ -1,5 +1,5 @@
// Tests that we'll emit the proper diagnostic message if we failed to find `::operator new(size_­t, nothrow_­t)`.
// RUN: %clang_cc1 %s -std=c++20 %s -fsyntax-only -verify
// RUN: %clang_cc1 %s -std=c++20 -Wno-coroutines-unsupported-target %s -fsyntax-only -verify
namespace std {
template <typename... T>

View file

@ -1,5 +1,5 @@
// Tests that we'll find aligned allocation function properly.
// RUN: %clang_cc1 %s -std=c++20 %s -fsyntax-only -verify -fcoro-aligned-allocation
// RUN: %clang_cc1 %s -std=c++20 %s -Wno-coroutines-unsupported-target -fsyntax-only -verify -fcoro-aligned-allocation
#include "Inputs/std-coroutine.h"
@ -109,7 +109,7 @@ task6 f5() { // expected-error 1+{{unable to find '::operator new(size_t, align_
co_return 43;
}
void *operator new(std::size_t, std::align_val_t, std::nothrow_t) noexcept;
void *operator new(std::size_t, std::align_val_t, std::nothrow_t) noexcept;
task6 f6() {
co_return 43;

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify
// RUN: %clang_cc1 %s -std=c++20 -Wno-coroutines-unsupported-target -fsyntax-only -verify
#include "Inputs/std-coroutine.h"
namespace std {

View file

@ -1,5 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
// RUN: %clang_cc1 -fsyntax-only -verify -DERRORS %s
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-coroutines-unsupported-target -std=c++20 %s
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-coroutines-unsupported-target -DERRORS %s
// Check that we don't crash when using __builtin_coro_* without the fcoroutine-ts or -std=c++20 option

View file

@ -1,5 +1,5 @@
// Tests that the behavior will be good if there are multiple operator delete in the promise_type.
// RUN: %clang_cc1 %s -std=c++20 %s -fsyntax-only -verify
// RUN: %clang_cc1 %s -std=c++20 -Wno-coroutines-unsupported-target %s -fsyntax-only -verify
// expected-no-diagnostics
#include "Inputs/std-coroutine.h"

View file

@ -1,7 +1,7 @@
// This file contains references to sections of the Coroutines TS, which can be
// found at http://wg21.link/coroutines.
// RUN: %clang_cc1 -std=c++20 -verify %s -fcxx-exceptions -fexceptions -Wunused-result
// RUN: %clang_cc1 -std=c++20 -verify %s -fcxx-exceptions -fexceptions -Wunused-result -Wno-coroutines-unsupported-target
namespace std {

View file

@ -1,6 +1,6 @@
// Test that if the compiler will emit error message if the promise_type contain
// operator delete but none of them are available. This is required by the standard.
// RUN: %clang_cc1 %s -std=c++20 %s -fsyntax-only -verify
// RUN: %clang_cc1 %s -std=c++20 %s -fsyntax-only -verify -Wno-coroutines-unsupported-target
#include "Inputs/std-coroutine.h"

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -Winvalid-noreturn -verify
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -Winvalid-noreturn -verify -Wno-coroutines-unsupported-target
#include "Inputs/std-coroutine.h"

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++20 -ast-dump %s | FileCheck %s
// RUN: %clang_cc1 -std=c++20 -Wno-coroutines-unsupported-target -ast-dump %s | FileCheck %s
#include "Inputs/std-coroutine.h"
// Github issue: https://github.com/llvm/llvm-project/issues/78290

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only -Wno-coroutines-unsupported-target %s
namespace std {
template <class Promise = void> struct coroutine_handle {

View file

@ -1,7 +1,7 @@
// This file contains references to sections of the Coroutines TS, which can be
// found at http://wg21.link/coroutines.
// RUN: %clang_cc1 -std=c++20 -verify %s -fcxx-exceptions -fexceptions -Wunused-result
// RUN: %clang_cc1 -std=c++20 -verify %s -fcxx-exceptions -fexceptions -Wunused-result -Wno-coroutines-unsupported-target
namespace std {

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify -Wno-coroutines-unsupported-target
#include "Inputs/std-coroutine.h"
struct MyTask{

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -Wno-vla-cxx-extension -verify
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -Wno-vla-cxx-extension -Wno-coroutines-unsupported-target -verify
#include "Inputs/std-coroutine.h"
struct promise;

View file

@ -0,0 +1,37 @@
// RUN: %clang_cc1 -std=c++20 -verify -triple i686-unknown-windows-msvc %s
namespace std {
template <typename... T> struct coroutine_traits;
template <class Promise = void> struct coroutine_handle {
coroutine_handle() = default;
static coroutine_handle from_address(void *) noexcept;
};
template <> struct coroutine_handle<void> {
static coroutine_handle from_address(void *) noexcept;
coroutine_handle() = default;
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
};
} // namespace std
struct suspend_always {
bool await_ready() noexcept;
void await_suspend(std::coroutine_handle<>) noexcept;
void await_resume() noexcept;
};
template <> struct std::coroutine_traits<void> {
struct promise_type {
void get_return_object() noexcept;
suspend_always initial_suspend() noexcept;
suspend_always final_suspend() noexcept;
void return_void() noexcept;
void unhandled_exception() noexcept;
};
};
void f() {
co_return; // expected-warning {{coroutines are not currently supported on the 32-bit x86 Microsoft ABI}}
}

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -verify %s -stdlib=libc++ -std=c++20 -fsyntax-only
// RUN: %clang_cc1 -verify %s -stdlib=libc++ -std=c++20 -fsyntax-only -Wno-coroutines-unsupported-target
namespace std {
template <class Promise = void>

View file

@ -1,8 +1,8 @@
// This file contains references to sections of the Coroutines TS, which can be
// found at http://wg21.link/coroutines.
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,cxx20_23,cxx23 %s -fcxx-exceptions -fexceptions -Wunused-result
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_23 %s -fcxx-exceptions -fexceptions -Wunused-result
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,cxx20_23,cxx23 %s -fcxx-exceptions -fexceptions -Wunused-result -Wno-coroutines-unsupported-target
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_23 %s -fcxx-exceptions -fexceptions -Wunused-result -Wno-coroutines-unsupported-target
// Run without -verify to check the order of errors we show.
// RUN: not %clang_cc1 -std=c++20 -fsyntax-only %s -fcxx-exceptions -fexceptions -Wunused-result 2>&1 | FileCheck %s

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s -Wno-coroutines-unsupported-target
#include "Inputs/std-coroutine.h"

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++2b %s -fsyntax-only -verify
// RUN: %clang_cc1 -std=c++2b %s -fsyntax-only -verify -Wno-coroutines-unsupported-target
#include "Inputs/std-coroutine.h"

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++20 %s
// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -std=c++20 %s -Wno-coroutines-unsupported-target
// expected-no-diagnostics

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++20 %s -fcxx-exceptions -fsyntax-only -Wexceptions -verify -fdeclspec
// RUN: %clang_cc1 -std=c++20 %s -fcxx-exceptions -fsyntax-only -Wexceptions -Wno-coroutines-unsupported-target -verify -fdeclspec
#include "Inputs/std-coroutine.h"

View file

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -Wunused-parameter -verify -std=c++20 %s
// RUN: %clang_cc1 -fsyntax-only -Wunused-parameter -verify -std=c++20 -Wno-coroutines-unsupported-target %s
#include "Inputs/std-coroutine.h"

View file

@ -999,8 +999,9 @@ C++23, informally referred to as C++26.</p>
<td><a href="https://wg21.link/p0912r5">P0912R5</a></td>
<td class="partial" align="center">
<details><summary>Partial</summary>
Fully supported on all targets except Windows, which
still has some stability and ABI issues.
Fully supported on all targets except Windows, which still has some stability and ABI issues.
On 32-bit x86 Windows, the <code>__cpp_impl_coroutine</code> feature macro is not defined,
and a warning is emitted when using coroutines.
</details></td>
</tr>
<!-- Cologne 2019 papers -->