-
Notifications
You must be signed in to change notification settings - Fork 19
Description
Summary
Two when_any tests fail on Apple Clang Release builds (-O2 and above) with default symbol visibility. The compiler-generated coroutine state machine silently drops exceptions thrown in the coroutine body, causing the run_async error handler to never fire.
Failing tests:
testEmptyVectorThrows—when_any.cpp:1348testEmptyRangeThrows—when_any.cpp:2157
Both verify that when_any(empty_range) delivers std::invalid_argument through the run_async error handler.
Environment
- Apple Clang 17.0.0 (clang-1700.0.13.3), arm64-apple-darwin25.3.0
- Failure boundary: passes at
-O1, fails at-O2and above -DNDEBUGand debug symbols have no effect
Root cause
Assembly diff between -O2 (failing) and -O2 -fvisibility=hidden (passing) shows the difference: with default visibility, template instantiation symbols (weak external linkage) are accessed through the GOT (@GOTPAGE/@GOTPAGEOFF). With hidden visibility, they use direct references (@PAGE/@PAGEOFF). The coroutine optimizer generates incorrect exception tables when these function references are GOT-indirect.
The bug is in compiler-generated code (the coroutine resume function's implicit try/catch that routes to unhandled_exception()), not in user-written code. This was confirmed by applying __attribute__((optnone)) to invoke_impl — the handler dispatch function — which had no effect, proving the exception never reaches it.
Approaches tried (all failed)
| Approach | Result |
|---|---|
__attribute__((optnone)) on invoke_impl |
FAIL |
__attribute__((noinline)) on exception() / unhandled_exception() |
FAIL |
volatile bool has_ep_ |
FAIL |
| Cache exception_ptr locally | FAIL |
asm volatile memory barrier on function pointer |
FAIL |
co_await noop IoAwaitable before throw |
FAIL |
[[noreturn]] __attribute__((noinline)) throw wrapper |
FAIL |
Current workaround
Build with -fvisibility=hidden on Apple Clang. This eliminates GOT indirection for weak template symbols and produces correct exception routing at all optimization levels.
Related upstream issues
- LLVM #61900 — incorrect code for coroutines when
unhandled_exception()exits via exception - LLVM #105595 — runtime crash at
-O2in coroutines withco_await - CWG2935 / PR #177628 — coroutine startup exception safety
TODO
- When a new Apple Clang version ships, test with default visibility at
-O2to check if the upstream fix has landed. Reproduce with:cmake -DCMAKE_CXX_FLAGS="-O2 -DNDEBUG" .. && cmake --build . && ctest - If fixed upstream, remove the
-fvisibility=hiddenworkaround from CI and document the minimum Apple Clang version