Open
Description
Following code when parsed and emitted with -ast-print
returns wrong result:
simple_coro<int> coro_test() {
co_yield 2001;
co_return 2010;
co_await my_awaitable{2061};
}
Result printed by clang is: (with fixed formatting
simple_coro<int> coro_test() {
co_yield __promise.yield_value(2001); // <-- ERROR HERE
co_return 2010;
co_await my_awaitable{2061};
}
Expected result:
simple_coro<int> coro_test() {
co_yield 2001;
co_return 2010;
co_await my_awaitable{2061};
}
Notice the co_yield __promise.yield_value(1997);
it contains call to __promise.yield_value
function, this helper AST is generated in SemaCoroutine.cpp
in Sema::ActOnCoyieldExpr
:
// Build yield_value call.
ExprResult Awaitable = buildPromiseCall(
*this, getCurFunction()->CoroutinePromise, Loc, "yield_value", E);
if (Awaitable.isInvalid())
return ExprError();
// Build 'operator co_await' call.
Awaitable = buildOperatorCoawaitCall(*this, S, Loc, Awaitable.get());
if (Awaitable.isInvalid())
return ExprError();
There is Operand slot inside CoroutineSuspendExpr
which should contain original expression, but the function BuildCoyieldExpr
is already getting modified AST.
Full source code to repeat the problem:
#include <coroutine>
struct my_awaitable {
int value;
bool await_ready() const {
return true;
}
void await_suspend(std::coroutine_handle<void>) {
}
int await_resume() const {
return value;
}
};
template <typename T> struct simple_coro {
struct promise_type {
T value{0};
constexpr auto initial_suspend() const noexcept {
return std::suspend_never{};
}
constexpr auto final_suspend() const noexcept {
return std::suspend_never{};
}
constexpr void return_value(int v) noexcept {
value = v;
}
constexpr auto yield_value(T v) noexcept {
value = v;
return std::suspend_always{};
}
std::coroutine_handle<promise_type> get_return_object() {
return std::coroutine_handle<promise_type>::from_promise(*this);
}
constexpr void unhandled_exception() const noexcept {
}
};
private:
std::coroutine_handle<promise_type> handle;
public:
constexpr simple_coro(std::coroutine_handle<promise_type> h) noexcept: handle{h} { }
constexpr ~simple_coro() noexcept {
handle.destroy();
}
constexpr int get() {
return handle.promise().value;
}
};
simple_coro<int> coro_test() {
co_yield 2001;
co_return 2010;
co_await my_awaitable{2061};
}
Maybe this is fine, but AFAIK clang is trying to represent the AST so the original source code is recoverable and this breaks it.