llvm / llvm/llvm-project

[clang-cl][x86] miscompiles `co_return std::string("hello")` in a coroutine returning `std::string`

Open
#193,161 5 comments 0 reactions 0 assignees View on GitHub
backend:X86 coroutines platform:windows
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

# clang-cl x86 miscompiles `co_return std::string("hello")` in a coroutine returning `std::string`

## Bug Summary

`clang-cl` on Windows x86 appears to miscompile this coroutine:

```cpp
Task run() { co_return std::string("hello"); }
```

It reproduces for me in both x86 Debug and x86 Release builds.

The same source works on the same machine with:

- `clang-cl` x64
- MSVC

## Reproducer

```cpp
#include
#include
#include
#include
#include

template
struct Task {
struct promise_type {
std::optional result;
Task get_return_object() { return { std::coroutine_handle::from_promise(*this) }; }
std::suspend_never initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_value(T value) { result.emplace(std::move(value)); }
void unhandled_exception() { std::terminate(); }
};

std::coroutine_handle h;
Task(std::coroutine_handle h) : h(h) {}
Task(Task&& other) noexcept : h(std::exchange(other.h, {})) {}
Task(const Task&) = delete;
Task& operator=(const Task&) = delete;
~Task() { if (h) h.destroy(); }
T take() { return std::move(*h.promise().result); }
};

Task run() { co_return std::string("hello"); }

int main() { return run().take() == "hello" ? 0 : 1; }
```

## Commands

Failing x86 Debug build:

```bat
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x86
"C:\Program Files\LLVM\bin\clang-cl.exe" ^
/nologo /std:c++20 /EHsc /Od /Zi /MDd /DDEBUG /clang:-m32 ^
repro.cpp
repro.exe
```

Failing x86 Release build:

```bat
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x86
"C:\Program Files\LLVM\bin\clang-cl.exe" ^
/nologo /std:c++20 /EHsc /O2 /DNDEBUG /MD /clang:-m32 ^
repro.cpp
repro.exe
```

Working control builds:

```bat
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
"C:\Program Files\LLVM\bin\clang-cl.exe" ^
/nologo /std:c++20 /EHsc /Od /Zi /MDd /DDEBUG ^
repro.cpp

call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x86
cl /nologo /std:c++20 /EHsc /Od /Zi /MDd /DDEBUG repro.cpp
```

## Expected Behavior

The program should exit with code `0`.

## Actual Behavior

With `clang-cl` x86, the program crashes with an access violation.

In the Debug build, the fault shows up while the returned `std::string` is being constructed or moved into the promise storage (`std::optional`).

## Stack Trace

From a failing x86 Debug build under `cdb`:

```text
clangcl_x86_coroutine_return_value_repro!std::basic_string,std::allocator >::_Swap_proxy_and_iterators
clangcl_x86_coroutine_return_value_repro!std::basic_string,std::allocator >::_Take_contents
clangcl_x86_coroutine_return_value_repro!std::basic_string,std::allocator >::basic_string
clangcl_x86_coroutine_return_value_repro!std::optional,std::allocator > >::emplace
clangcl_x86_coroutine_return_value_repro!Task,std::allocator > >::promise_type::return_value
clangcl_x86_coroutine_return_value_repro!run
clangcl_x86_coroutine_return_value_repro!main
```

Relevant source frames from my local build:

```text
Task::promise_type::return_value (clangcl_x86_coroutine_return_value_repro.cpp:28)
run (clangcl_x86_coroutine_return_value_repro.cpp:41)
main (clangcl_x86_coroutine_return_value_repro.cpp:43)
```

## Environment

- OS: Microsoft Windows 10.0.19045.6456
- `clang-cl --version`:

```text
clang version 21.1.8
Target: i386-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\LLVM\bin
```

- MSVC x86 compiler version:

```text
Microsoft (R) C/C++ Optimizing Compiler Version 19.44.35225 for x86
```

- MSVC linker version:

```text
Microsoft (R) Incremental Linker Version 14.44.35225.0
```

## Reduction Notes

I started from a larger reproducer involving:

- a custom wrapper type containing `std::string`
- a separate `make(std::string)` helper
- `co_return make("hello")`

After reduction, these are **not required** in my tests:

- a custom wrapper type
- a separate callee function
- a callee taking `std::string` by value

I also tested a variant where `promise_type::return_value` takes `T&&` instead of `T value`, and that still reproduced, so I am intentionally **not** claiming that pass-by-value in `return_value` is a necessary condition.

Contributor guide

Open the contributing guide

Research direction

Start with the standalone repro.cpp and run the failing clang-cl x86 Debug and Release commands, then compare them with the working clang-cl x64 and MSVC builds. Focus investigation on the coroutine run function and promise_type::return_value path shown in the stack trace. Done means the x86 executable exits with code 0 without an access violation.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.