Skip to content

CoyieldExpr doesn't contain the original AST -ast-print returns invalid source. #147150

Open
@hanickadot

Description

@hanickadot

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:frontendLanguage frontend issues, e.g. anything involving "Sema"

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions