]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++, coroutines: Fix identification of coroutine ramps [PR120453].
authorIain Sandoe <iain@sandoe.co.uk>
Thu, 29 May 2025 10:00:18 +0000 (11:00 +0100)
committerIain Sandoe <iain@sandoe.co.uk>
Thu, 29 May 2025 15:14:16 +0000 (16:14 +0100)
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 <iain@sandoe.co.uk>
gcc/cp/cp-tree.h
gcc/cp/typeck.cc
gcc/testsuite/g++.dg/coroutines/pr120453.C [new file with mode: 0644]

index 19c0b452d8689d96c67317e101891f96ccd5c8c6..d9fc80b92e5822323f113f18f9f56b347a6cb878 100644 (file)
@@ -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) \
index af2cbaff8fdc703cb5b7e898cc17e5ad8be76816..ac1eb397f0113e99a201679549ba83f908e6f097 100644 (file)
@@ -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 (file)
index 0000000..2f2c4ec
--- /dev/null
@@ -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