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

-- 8< --

Here the internal guard variables that control whether to clean up the
structured binding variables were not living across the call to co_await
because they weren't promoted into the coroutine frame.  The underlying
problem is using a TARGET_EXPR, which lives only for the full-expression, to
hold a value that needs to live as long as the variable itself.  So
get_temp_regvar seems like a better fit.  We also need to manually pushdecl
the guard variable so that it's visible to register_local_var_uses.

It might be better to use the wrap_temporary_cleanups mechanism for the main
variable, as we do for normal variables.  But let's go with the simple fix
for now.

        PR c++/124584

gcc/cp/ChangeLog:

        * decl.cc (cp_finish_decl): Use get_temp_regvar for decomp guards.

gcc/testsuite/ChangeLog:

        * g++.dg/coroutines/tuple-decomp-pr124584.C: New test.
---
 gcc/cp/decl.cc                                | 15 +++---
 .../torture/tuple-decomp-pr124584.C           | 54 +++++++++++++++++++
 2 files changed, 63 insertions(+), 6 deletions(-)
 create mode 100644 
gcc/testsuite/g++.dg/coroutines/torture/tuple-decomp-pr124584.C

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index dbe4fa44ce5..c2a75c669c7 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -10108,10 +10108,14 @@ cp_finish_decl (tree decl, tree init, bool 
init_const_expr_p,
              tree guard = NULL_TREE;
              if (cleanups || cleanup)
                {
-                 guard = get_internal_target_expr (boolean_false_node);
-                 add_stmt (guard);
-                 guard = TARGET_EXPR_SLOT (guard);
+                 /* Since the CLEANUP_STMT will refer to the guard, we need it
+                     to be a variable with the same lifetime.  ??? It might be
+                     better to use wrap_temporary_cleanups.  */
+                 guard = get_temp_regvar (boolean_type_node, 
boolean_false_node);
+                 /* And make sure register_local_var_uses sees it.  */
+                 pushdecl (guard);
                }
+
              tree sl = push_stmt_list ();
              initialize_local_var (decl, init, true);
              if (guard)
@@ -10137,9 +10141,8 @@ cp_finish_decl (tree decl, tree init, bool 
init_const_expr_p,
                     popped that all, so push those extra cleanups around
                     the whole sequence with a guard variable.  */
                  gcc_assert (TREE_CODE (sl) == STATEMENT_LIST);
-                 guard = get_internal_target_expr (integer_zero_node);
-                 add_stmt (guard);
-                 guard = TARGET_EXPR_SLOT (guard);
+                 guard = get_temp_regvar (integer_type_node, 
integer_zero_node);
+                 pushdecl (guard);
                  for (unsigned i = 0; i < n_extra_cleanups; ++i)
                    {
                      tree_stmt_iterator tsi = tsi_last (sl);
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/tuple-decomp-pr124584.C 
b/gcc/testsuite/g++.dg/coroutines/torture/tuple-decomp-pr124584.C
new file mode 100644
index 00000000000..8679d52bf09
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/tuple-decomp-pr124584.C
@@ -0,0 +1,54 @@
+#include <coroutine>
+#include <tuple>
+
+static int g_dtor_count = 0;
+
+struct guard {
+  bool live_ = true;
+  ~guard() { if (live_) ++g_dtor_count; }
+  guard() = default;
+  guard(guard&& o) noexcept
+    : live_(o.live_) {o.live_ = false;}
+};
+
+static std::coroutine_handle<> g_pending;
+
+struct yield_aw {
+  bool await_ready() const noexcept { return false; }
+  void await_suspend(std::coroutine_handle<> h) noexcept
+  { g_pending = h; }
+  void await_resume() const noexcept {}
+};
+
+struct task {
+  struct promise_type {
+    task get_return_object() noexcept {
+      return {std::coroutine_handle<
+       promise_type>::from_promise(*this)};
+    }
+    std::suspend_never  initial_suspend() noexcept { return {}; }
+    std::suspend_always final_suspend()   noexcept { return {}; }
+    void return_void() noexcept {}
+    void unhandled_exception() { __builtin_abort(); }
+  };
+  std::coroutine_handle<promise_type> h_;
+  ~task() { if (h_) h_.destroy(); }
+};
+
+std::tuple<guard> make_guarded_tuple() { return {guard{}}; }
+
+task test_fn() {
+  auto [g] = make_guarded_tuple();   // not co_await
+  co_await yield_aw{};                // binding crosses suspend
+  (void)g;
+}
+
+int main() {
+  auto t = test_fn();
+  auto h = g_pending;
+  g_pending = nullptr;
+  h.resume();
+  __builtin_printf("dtor_count = %d  %s\n",
+                  g_dtor_count, g_dtor_count > 0 ? "OK" : "BUG");
+  return g_dtor_count > 0 ? 0 : 1;
+}

base-commit: 7b5a05667c8e40bec5e070b1638d3b3882f7c78e
-- 
2.54.0

Reply via email to