| Issue |
166518
|
| Summary |
[Clang] C++20 coroutine-related program miscompiled into segmentation fault only when above -O2 and without UBSan
|
| Labels |
clang
|
| Assignees |
|
| Reporter |
vspefs
|
## Situation
A program (code at the end) that seems logically correct runs into segmentation fault only when compiled with above `-O2` level optimization and without UBSan on. That is, the program compiles and runs as expected when
```bash
$ clang++ -std=c++23 -stdlib=libc++ -O{0,1} ./program.cc
$ ./a.out
Awaiting answer...
Constructing answer...
43
```
and
```bash
$ clang++ -std=c++23 -stdlib=libc++ -O{2,3} -fsanitize=undefined ./program.cc
$ ./a.out
Awaiting answer...
Constructing answer...
43
```
but not when
```bash
$ clang++ -std=c++23 -stdlib=libc++ -O{2,3} ./program.cc
$ ./a.out
Awaiting answer...
[SEG FAULT HERE]
```
It compiles and runs as expected under GCC with any optimization level, UBSan on or not.
GDB traces the segmentation fault to a call to `__builtin_coro_resume()` call, so I guess it's coroutine-related. I checked with my very low IQ and decided it's not UB but a compiler bug.
## Environment
Both on:
> System: Arch Linux (kernel version 6.17.7-arch1-1)
> Architecture: x86_64
> LLVM/Clang version: 21.1.4-1 from Arch official repo
and
> System: Windows 11 Home
> MSYS2 environment: MSYS2 CLANG64
> LLVM/Clang version: 21.1.4-1 from MSYS2 official repo
## Program
```c++
#include <print>
#include <coroutine>
#include <optional>
#include <exception>
// LIBRARY CODE
template <typename R>
class inline_construct
{
public:
class promise_type;
class awaitable_type;
using result_type = R;
using handle_type = std::coroutine_handle<promise_type>;
public:
awaitable_type operator co_await ();
private:
inline_construct (handle_type handle);
handle_type handle_;
};
template <typename R>
class inline_construct<R>::promise_type
{
public:
inline_construct get_return_object ();
std::suspend_always initial_suspend ();
std::suspend_always final_suspend () noexcept;
void unhandled_exception ();
void return_value (result_type value);
result_type extract_result ();
private:
std::optional<result_type> ret_;
std::exception_ptr exception_;
};
template <typename R>
class inline_construct<R>::awaitable_type
{
public:
friend class inline_construct;
public:
bool await_ready ();
bool await_suspend (std::coroutine_handle<>);
result_type await_resume ();
private:
awaitable_type (inline_construct &construct);
inline_construct &construct_;
};
template <typename R>
inline_construct<R>::inline_construct (handle_type handle)
: handle_ (handle)
{
}
template <typename R>
auto
inline_construct<R>::operator co_await ()
-> awaitable_type
{
return awaitable_type (*this);
}
template <typename R>
auto
inline_construct<R>::promise_type::get_return_object ()
-> inline_construct
{
return inline_construct (handle_type::from_promise (*this));
}
template <typename R>
std::suspend_always
inline_construct<R>::promise_type::initial_suspend ()
{
return std::suspend_always ();
}
template <typename R>
std::suspend_always
inline_construct<R>::promise_type::final_suspend () noexcept
{
return std::suspend_always ();
}
template <typename R>
void
inline_construct<R>::promise_type::unhandled_exception ()
{
exception_ = std::current_exception ();
}
template <typename R>
void
inline_construct<R>::promise_type::return_value (result_type ret)
{
ret_.emplace (std::move (ret));
}
template <typename R>
auto
inline_construct<R>::promise_type::extract_result ()
-> result_type
{
if (exception_)
std::rethrow_exception (exception_);
return std::move (*std::move (ret_));
}
template <typename R>
inline_construct<R>::awaitable_type::awaitable_type (inline_construct &construct)
: construct_ (construct)
{
}
template <typename R>
bool
inline_construct<R>::awaitable_type::await_ready ()
{
return false;
}
template <typename R>
bool
inline_construct<R>::awaitable_type::await_suspend (std::coroutine_handle<>)
{
return false;
}
template <typename R>
auto
inline_construct<R>::awaitable_type::await_resume ()
-> result_type
{
construct_.handle_.resume ();
result_type ret = construct_.handle_.promise ().extract_result ();
construct_.handle_.destroy ();
return std::move (ret);
}
// APPLICATION CODE
struct promise;
struct coroutine : std::coroutine_handle<promise>
{
using promise_type = ::promise;
};
struct promise
{
coroutine
get_return_object()
{
return coroutine (coroutine::from_promise (*this));
}
std::suspend_always
initial_suspend () noexcept
{
return std::suspend_always ();
}
std::suspend_always
final_suspend() noexcept
{
return std::suspend_always ();
}
void return_void ()
{
}
void unhandled_exception()
{
std::terminate ();
}
};
coroutine
test ()
{
auto answer = [] -> inline_construct<int>
{
std::println ("Constructing answer...");
co_return 42;
} ();
auto plus_one = [&] -> inline_construct<int>
{
std::println ("Awaiting answer...");
co_return 1 + co_await answer;
} ();
std::println ("{}", co_await plus_one);
co_return;
}
int
main ()
{
coroutine coro = test ();
coro.resume ();
coro.destroy ();
}
```
##
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs