https://gcc.gnu.org/g:217b7f655227a52e5fe26729baa09dc6083ed577
commit r16-961-g217b7f655227a52e5fe26729baa09dc6083ed577 Author: Iain Sandoe <i...@sandoe.co.uk> Date: Thu May 29 11:00:18 2025 +0100 c++, coroutines: Fix identification of coroutine ramps [PR120453]. The existing implementation, incorrectly, tried to use DECL_RAMP_FN in check_return_expr to determine if we are handling a ramp func. However, that query is only set for the resume/destroy functions. Replace the use of DECL_RAMP_FN with a new query. PR c++/120453 gcc/cp/ChangeLog: * cp-tree.h (DECL_RAMP_P): New. * typeck.cc (check_return_expr): Use DECL_RAMP_P instead of DECL_RAMP_FN. gcc/testsuite/ChangeLog: * g++.dg/coroutines/pr120453.C: New test. Signed-off-by: Iain Sandoe <i...@sandoe.co.uk> Diff: --- gcc/cp/cp-tree.h | 4 ++ gcc/cp/typeck.cc | 2 +- gcc/testsuite/g++.dg/coroutines/pr120453.C | 95 ++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 19c0b452d868..d9fc80b92e58 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5522,6 +5522,10 @@ decl_template_parm_check (const_tree t, const char *f, int l, const char *fn) #define DECL_RAMP_FN(NODE) \ (coro_get_ramp_function (NODE)) +/* For a FUNCTION_DECL this is true if it is a coroutine ramp. */ +#define DECL_RAMP_P(NODE) \ + DECL_COROUTINE_P (NODE) && !DECL_RAMP_FN (NODE) + /* True for an OMP_ATOMIC that has dependent parameters. These are stored as an expr in operand 1, and integer_zero_node or clauses in operand 0. */ #define OMP_ATOMIC_DEPENDENT_P(NODE) \ diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index af2cbaff8fdc..ac1eb397f011 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -11466,7 +11466,7 @@ check_return_expr (tree retval, bool *no_warning, bool *dangling) /* Don't check copy-initialization for NRV in a coroutine ramp; we implement this case as NRV, but it's specified as directly initializing the return value from get_return_object(). */ - if (DECL_RAMP_FN (current_function_decl) && named_return_value_okay_p) + if (DECL_RAMP_P (current_function_decl) && named_return_value_okay_p) converted = true; /* First convert the value to the function's return type, then diff --git a/gcc/testsuite/g++.dg/coroutines/pr120453.C b/gcc/testsuite/g++.dg/coroutines/pr120453.C new file mode 100644 index 000000000000..2f2c4ece008b --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr120453.C @@ -0,0 +1,95 @@ +// PR120453 - reduced testcase amended to add a TaskBase move constructor +// and a LazyTask destructor, to more closely match the original code. +// { dg-additional-options "-w" } +namespace std { +template <typename> struct __coroutine_traits_impl; +template <typename _Result> + requires requires { typename _Result; } +struct __coroutine_traits_impl<_Result>; +template <typename _Result, typename> struct coroutine_traits : _Result {}; +template <typename = void> struct coroutine_handle { + static coroutine_handle from_address(void *); + operator coroutine_handle<>(); + void *address(); +}; +struct suspend_never { + bool await_ready(); + void await_suspend(coroutine_handle<>); + void await_resume(); +}; +} // namespace std + +namespace QCoro { +namespace detail { +template <typename T> +concept has_await_methods = requires(T t) { t; }; +} // namespace detail + +template <typename T> +concept Awaitable = detail::has_await_methods<T>; +namespace detail { +struct TaskFinalSuspend { + bool await_ready() noexcept; + template <typename Promise> + void await_suspend(std::coroutine_handle<Promise>) noexcept; + void await_resume() noexcept; +}; +struct TaskPromiseBase { + std::suspend_never initial_suspend(); + auto final_suspend() noexcept { return TaskFinalSuspend{}; } + template <Awaitable T> auto &&await_transform(T &&); +}; +struct TaskPromise : TaskPromiseBase { + void unhandled_exception(); +}; +template <typename> struct TaskAwaiterBase { + bool await_ready(); + void await_suspend(std::coroutine_handle<>); +}; +template <typename, template <typename> class, typename> struct TaskBase { + TaskBase() = default; + TaskBase(TaskBase &&) = default; + void operator=(TaskBase &&); + auto operator co_await() const; + std::coroutine_handle<> mCoroutine; +}; +} // namespace detail +template <typename> struct Task : detail::TaskBase<int, Task, int> {}; +} // namespace QCoro + +namespace QCoro::detail { +template <Awaitable T> auto &&TaskPromiseBase::await_transform(T &&awaitable) { + return awaitable; +} + +template <typename T, template <typename> class TaskImpl, typename PromiseType> +auto TaskBase<T, TaskImpl, PromiseType>::operator co_await() const { + class TaskAwaiter : public TaskAwaiterBase<PromiseType> { + public: + TaskAwaiter(std::coroutine_handle<>); + auto await_resume() {} + }; + return TaskAwaiter{mCoroutine}; +} + +} // namespace QCoro::detail + +namespace QCoro { +template <typename T> class LazyTask ; +namespace detail { +struct LazyTaskPromise : TaskPromise { + LazyTask<int> get_return_object(); +}; +} // namespace detail + +template <typename T> struct LazyTask : detail::TaskBase<int, LazyTask, int> { + typedef detail::LazyTaskPromise promise_type; + ~LazyTask(); +}; + +auto coro = [] -> LazyTask<int> +{ + co_await [] -> Task<int> { return {}; }(); +}; + +} // namespace QCoro