llvm / llvm/llvm-project

Exceptions from std::async cannot be caught on macOS ARM with Clang 18+

Open
#168,287 10 comments 0 reactions 0 assignees View on GitHub
c++11 clang libc++ lld:MachO platform:macos
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.