From: Iain Sandoe Date: Thu, 29 May 2025 10:00:18 +0000 (+0100) Subject: c++, coroutines: Fix identification of coroutine ramps [PR120453]. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=217b7f655227a52e5fe26729baa09dc6083ed577;p=thirdparty%2Fgcc.git 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 --- diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 19c0b452d86..d9fc80b92e5 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 af2cbaff8fd..ac1eb397f01 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 00000000000..2f2c4ece008 --- /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 struct __coroutine_traits_impl; +template + requires requires { typename _Result; } +struct __coroutine_traits_impl<_Result>; +template struct coroutine_traits : _Result {}; +template 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 +concept has_await_methods = requires(T t) { t; }; +} // namespace detail + +template +concept Awaitable = detail::has_await_methods; +namespace detail { +struct TaskFinalSuspend { + bool await_ready() noexcept; + template + void await_suspend(std::coroutine_handle) noexcept; + void await_resume() noexcept; +}; +struct TaskPromiseBase { + std::suspend_never initial_suspend(); + auto final_suspend() noexcept { return TaskFinalSuspend{}; } + template auto &&await_transform(T &&); +}; +struct TaskPromise : TaskPromiseBase { + void unhandled_exception(); +}; +template struct TaskAwaiterBase { + bool await_ready(); + void await_suspend(std::coroutine_handle<>); +}; +template class, typename> struct TaskBase { + TaskBase() = default; + TaskBase(TaskBase &&) = default; + void operator=(TaskBase &&); + auto operator co_await() const; + std::coroutine_handle<> mCoroutine; +}; +} // namespace detail +template struct Task : detail::TaskBase {}; +} // namespace QCoro + +namespace QCoro::detail { +template auto &&TaskPromiseBase::await_transform(T &&awaitable) { + return awaitable; +} + +template class TaskImpl, typename PromiseType> +auto TaskBase::operator co_await() const { + class TaskAwaiter : public TaskAwaiterBase { + public: + TaskAwaiter(std::coroutine_handle<>); + auto await_resume() {} + }; + return TaskAwaiter{mCoroutine}; +} + +} // namespace QCoro::detail + +namespace QCoro { +template class LazyTask ; +namespace detail { +struct LazyTaskPromise : TaskPromise { + LazyTask get_return_object(); +}; +} // namespace detail + +template struct LazyTask : detail::TaskBase { + typedef detail::LazyTaskPromise promise_type; + ~LazyTask(); +}; + +auto coro = [] -> LazyTask +{ + co_await [] -> Task { return {}; }(); +}; + +} // namespace QCoro