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

Reply via email to