]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: coroutines and return in registers [PR118874]
authorJason Merrill <jason@redhat.com>
Wed, 5 Mar 2025 13:45:34 +0000 (08:45 -0500)
committerJason Merrill <jason@redhat.com>
Wed, 5 Mar 2025 16:58:56 +0000 (11:58 -0500)
Because coroutines insert a call to the resumer between the initialization
of the return value and the actual return to the caller, we need to
duplicate the work of gimplify_return_expr for the !aggregate_value_p case.

PR c++/117364
PR c++/118874

gcc/cp/ChangeLog:

* coroutines.cc (cp_coroutine_transform::build_ramp_function): For
!aggregate_value_p return force return value into a local temp.

gcc/testsuite/ChangeLog:

* g++.dg/coroutines/torture/pr118874.C: New test.

Co-authored-by: Jakub Jelinek <jakub@redhat.com>
gcc/cp/coroutines.cc
gcc/testsuite/g++.dg/coroutines/torture/pr118874.C [new file with mode: 0644]

index d3c7ff3bd72d9a21765fa4a92b022aeb1f8b83fb..b92d09fa4ead001b82381b4dbbec8efb662044aa 100644 (file)
@@ -5161,6 +5161,7 @@ cp_coroutine_transform::build_ramp_function ()
 
   /* Used for return objects in the RESULT slot.  */
   tree ret_val_dtor = NULL_TREE;
+  tree retval = NULL_TREE;
 
   /* [dcl.fct.def.coroutine] / 7
      The expression promise.get_return_object() is used to initialize the
@@ -5189,6 +5190,21 @@ cp_coroutine_transform::build_ramp_function ()
       /* Check for bad things.  */
       if (!r || r == error_mark_node)
        return false;
+      if (!aggregate_value_p (fn_return_type, orig_fn_decl)
+         && TREE_CODE (r) == INIT_EXPR)
+       {
+         /* If fn_return_type doesn't need to be returned in memory, normally
+            gimplify_return_expr redirects the INIT_EXPR to a temporary.  But
+            r isn't wrapped in the RETURN_EXPR, so we need to do the
+            redirection here as well.  See PR118874.  */
+         tree temp = create_temporary_var (fn_return_type);
+         add_decl_expr (temp);
+         retval = copy_node (r);
+         TREE_OPERAND (r, 0) = temp;
+         TREE_OPERAND (retval, 1) = temp;
+       }
+      else
+       retval = DECL_RESULT (orig_fn_decl);
     }
 
   finish_expr_stmt (r);
@@ -5215,7 +5231,7 @@ cp_coroutine_transform::build_ramp_function ()
   /* The ramp is done, we just need the return statement, which we build from
      the return object we constructed before we called the actor.  */
 
-  r = void_ramp_p ? NULL_TREE : DECL_RESULT (orig_fn_decl);
+  r = retval;
 
   /* The reminder of finish_return_expr ().  */
   r = build_stmt (loc, RETURN_EXPR, r);
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/pr118874.C b/gcc/testsuite/g++.dg/coroutines/torture/pr118874.C
new file mode 100644 (file)
index 0000000..925d56a
--- /dev/null
@@ -0,0 +1,33 @@
+// PR c++/118874
+// { dg-do compile }
+// { dg-additional-options "-std=c++20" }
+
+#include <coroutine>
+
+struct B {
+  bool await_ready () const noexcept;
+  void await_suspend (std::coroutine_handle<> h) const noexcept;
+  void await_resume () const noexcept;
+};
+
+struct C {
+  struct promise_type {
+    const char *value;
+    std::suspend_never initial_suspend ();
+    std::suspend_always final_suspend () noexcept;
+    void return_value (const char *v);
+    void unhandled_exception ();
+    C get_return_object () { return C{this}; }
+  };
+  promise_type *p;
+  explicit C (promise_type *p) : p(p) {}
+  const char *get ();
+};
+
+C
+bar (bool x)
+{
+  if (x)
+    co_await B{};
+  co_return "foobar";
+}