]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Fix ICE due to shared BLOCK node in coroutine generation [PR103328]
authorBenno Evers <benno.evers@tenzir.com>
Sat, 2 Apr 2022 16:22:33 +0000 (17:22 +0100)
committerRichard Biener <rguenther@suse.de>
Thu, 7 Apr 2022 10:41:21 +0000 (12:41 +0200)
When finishing a function that is a coroutine, the function is
transformed into a "ramp" function, and the original user-provided
function body gets moved into a newly created "actor" function.

In this case `current_function_decl` points to the ramp function,
but `current_binding_level->blocks` would still point to the
scope block of the user-provided function body in the actor function,
so when the ramp function was finished during `poplevel()` in decl.cc,
we could end up with that block being reused as the `DECL_INITIAL()` of
the ramp function:

   subblocks = functionbody >= 0 ? current_binding_level->blocks : 0;
   // [...]
   DECL_INITIAL (current_function_decl) = block ? block : subblocks;

This block would then be independently modified by subsequent passes
touching either the ramp or the actor function, potentially causing
an ICE depending on the order and function of these passes.

gcc/cp/ChangeLog:

PR c++/103328
* coroutines.cc (morph_fn_to_coro): Reset
current_binding_level->blocks.

gcc/testsuite/ChangeLog:

PR c++/103328
* g++.dg/coroutines/pr103328.C: New test.

Co-Authored-By: Iain Sandoe <iain@sandoe.co.uk>
(cherry picked from commit 0847ad33b908af88bca1e6980d0b977316d05e18)

gcc/cp/coroutines.cc
gcc/testsuite/g++.dg/coroutines/pr103328.C [new file with mode: 0644]

index 233c264c24bbb31f05c1305a3f8598c14dd730e1..e982cdb89a77c6daf8bdc087f23ce07a3bf37858 100644 (file)
@@ -4516,6 +4516,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   BIND_EXPR_BLOCK (ramp_bind) = top_block;
   BLOCK_VARS (top_block) = BIND_EXPR_VARS (ramp_bind);
   BLOCK_SUBBLOCKS (top_block) = NULL_TREE;
+  current_binding_level->blocks = top_block;
 
   /* The decl_expr for the coro frame pointer, initialize to zero so that we
      can pass it to the IFN_CO_FRAME (since there's no way to pass a type,
diff --git a/gcc/testsuite/g++.dg/coroutines/pr103328.C b/gcc/testsuite/g++.dg/coroutines/pr103328.C
new file mode 100644 (file)
index 0000000..56fb54a
--- /dev/null
@@ -0,0 +1,32 @@
+// { dg-additional-options "-g" }
+
+#include <coroutine>
+
+struct task {
+  struct promise_type {
+    task get_return_object() { return {}; }
+    std::suspend_never initial_suspend() { return {}; }
+    std::suspend_never final_suspend() noexcept { return {}; }
+    void unhandled_exception() {}
+  };
+  bool await_ready() { return false; }
+  void await_suspend(std::coroutine_handle<> h) {}
+  void await_resume() {}
+};
+
+template <typename Func>
+void call(Func func) { func(); }
+
+class foo {
+  void f();
+  task g();
+};
+
+void foo::f() {
+  auto lambda = [this]() noexcept -> task {
+      co_await g();
+  };
+  (void)call<decltype(lambda)>;
+}
+
+int main() {}