Exceptions from std::async cannot be caught on macOS ARM with Clang 18+
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
# Exceptions from std::async cannot be caught on macOS ARM with Clang 18+
## Summary
On macOS ARM with Clang 18+, exceptions thrown from `std::async` contexts cannot be caught properly. The exception escapes all catch blocks and causes program termination with `libc++abi: terminating due to uncaught exception`.
**Important Finding**: This bug is **NOT module-specific**. It affects both C++ modules and regular C++ code when using `std::async`.
## Environment
- **Platform**: macOS ARM (Apple Silicon)
- **Compiler**: Clang 18+ (tested with Clang 21.1.5)
- **Standard Library**: libc++ (Homebrew LLVM)
- **Optimization**: All optimization levels (`-O0`, `-O1`, `-O2`, `-O3`) - bug occurs regardless
## Minimal Reproduction
```cpp
#include
#include
#include
struct custom_exception : public std::runtime_error {
custom_exception() : std::runtime_error("custom_exception") {}
};
// Key: The std::async call must be in a separate function to trigger the bug
void throw_from_async() {
auto future = std::async(std::launch::async, []() {
throw custom_exception{};
});
future.get();
}
int main() {
try {
throw_from_async();
}
catch (const custom_exception& e) {
std::cout << "SUCCESS: Exception caught: " << e.what() << "\n";
return 0;
}
catch (...) {
std::cout << "FAILED: Exception not caught\n";
return 1;
}
}
```
**Compile:**
```bash
clang++ -std=c++20 -stdlib=libc++ -O3 \
-L/opt/homebrew/opt/llvm/lib/c++ -L/opt/homebrew/opt/llvm/lib \
-Wl,-rpath,/opt/homebrew/opt/llvm/lib/c++ -Wl,-rpath,/opt/homebrew/opt/llvm/lib \
-lc++ \
minimal_repro.cpp -o minimal_repro
```
Or use the provided build script: [minimal_repro.sh](https://gist.github.com/ruoka/9fe0064746aaacba4c7de2441b2b7a0e)
**Expected**: Exception is caught, prints "SUCCESS: Exception caught"
**Actual**: Program terminates with:
```
libc++abi: terminating due to uncaught exception of type custom_exception: custom_exception
```
## Root Cause Analysis
Based on investigation, this appears to be related to:
1. **LLVM built-in unwinder**: The bug is specific to macOS ARM where LLVM's built-in unwinder is enabled by default in Homebrew Clang 18+
2. **Exception propagation across threads**: The issue manifests specifically with `std::async`, suggesting problems with exception unwinding across thread boundaries
3. **Function boundaries**: The bug requires the `std::async` call to be in a separate function (not inlined into `main()`)
## Additional Context
- The bug does **not** occur on Linux ARM (e.g., Asahi Linux) with the same Clang version
- The bug does **not** occur with Clang < 18
- The bug affects both module and non-module code
- The bug persists even with explicit RTTI flags (`-frtti -fexceptions -fcxx-exceptions`)
- **The bug occurs at all optimization levels** (`-O0`, `-O1`, `-O2`, `-O3`) - optimization level does not affect the bug
## Workaround
**No known workaround**: The bug occurs at all optimization levels (`-O0` through `-O3`).
The only potential workaround is to avoid using `std::async` with exceptions on macOS ARM, or to catch exceptions within the async lambda itself before they propagate across thread boundaries.
## Related Issues
This may be related to or a duplicate of:
- [llvm/llvm-project#92121](https://github.com/llvm/llvm-project/issues/92121) - Exception handling issues on macOS ARM
## Test Cases
A comprehensive test suite demonstrating the bug is available. The minimal reproduction code is included above, and test files are available as GitHub Gists:
- **Minimal reproduction**: [minimal_repro.cpp](https://gist.github.com/ruoka/c98138bc41fbda66b7c2e64221b17dfb) (44 lines, also included above)
- **Build script**: [minimal_repro.sh](https://gist.github.com/ruoka/9fe0064746aaacba4c7de2441b2b7a0e) - Automated build and test script
- Full test suite with 11 test cases covering various exception patterns
- Non-module test confirming bug is not module-specific
## Steps to Reproduce
1. On macOS ARM, install Homebrew LLVM: `brew install llvm`
2. Compile the minimal reproduction code above with `-O3`
3. Run the executable
4. Observe program termination instead of exception being caught
## Impact
This bug affects any code using `std::async` with exception handling on macOS ARM, making it impossible to properly handle exceptions from asynchronous operations. This is a critical issue for production code relying on exception safety in concurrent contexts.
Contributor guide
Research direction
Start by running minimal_repro.cpp with minimal_repro.sh on macOS ARM using the stated Clang and libc++ configuration, then compare the expected caught exception with the termination output. Review the full test suite and related issue llvm/llvm-project#92121 for relevant exception-handling context. Done means the reproduction catches the exception correctly without regressing the other listed cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100