Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

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 <ja...@redhat.com>
---
 gcc/cp/coroutines.cc                          | 18 +++++++++-
 .../g++.dg/coroutines/torture/pr118874.C      | 33 +++++++++++++++++++
 2 files changed, 50 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/pr118874.C

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index d3c7ff3bd72..b92d09fa4ea 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -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
index 00000000000..925d56ae2ea
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/pr118874.C
@@ -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";
+}

base-commit: 556e25f0e9abc720c940994bd9a1491062933d49
-- 
2.48.1

Reply via email to