在 2020/3/2 下午10:49, Nathan Sidwell 写道:
On 2/12/20 2:23 AM, JunMa wrote:
Hi
In captures_temporary, the current implementation fails to handle
component_ref. This causes ice with case co_await A while
operator co_await is defined in base class of A. Also it is necessary
to capture the object of base class as if it is temporary object.
This patch strips component_ref to its base object and check it as
usual.
Bootstrap and test on X86_64, is it OK?
Regards
JunMa
gcc/cp
2020-02-12 Jun Ma <ju...@linux.alibaba.com>
* coroutines.cc (captures_temporary): Strip component_ref
to its base object.
gcc/testsuite
2020-02-12 Jun Ma <ju...@linux.alibaba.com>
* g++.dg/coroutines/torture/co-await-15-capture-comp-ref.C:
New test.
+
+ /* In case of component_ref, we need to capture the object of
base
+ class as if it is temporary object. There are two possibilities:
+ (*base).field and base->field. */
+ while (TREE_CODE (parm) == COMPONENT_REF)
+ {
+ parm = TREE_OPERAND (parm, 0);
+ if (TREE_CODE (parm) == INDIRECT_REF)
+ parm = TREE_OPERAND (parm, 0);
+ while (TREE_CODE (parm) == NOP_EXPR)
+ parm = TREE_OPERAND (parm, 0);
Use STRIP_NOPS.
+ }
+
if (TREE_CODE (parm) == VAR_DECL && !DECL_ARTIFICIAL (parm))
/* This isn't a temporary... */
continue;
- if (TREE_CODE (parm) == PARM_DECL)
+ if (TREE_CODE (parm) == PARM_DECL || TREE_CODE (parm) ==
NON_LVALUE_EXPR)
/* .. nor is this... */
continue;
Either a separate if, or merging both ifs (my preference) would be
better.
nathan
Hi nathan
Here is the updated patch
Regards
JunMa
---
gcc/cp/coroutines.cc | 20 +++-
.../torture/co-await-15-capture-comp-ref.C | 99 +++++++++++++++++++
2 files changed, 114 insertions(+), 5 deletions(-)
create mode 100644
gcc/testsuite/g++.dg/coroutines/torture/co-await-15-capture-comp-ref.C
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 966ec0583aa..2a54bcefc1e 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -2613,12 +2613,22 @@ captures_temporary (tree *stmt, int *do_subtree, void
*d)
continue;
parm = TREE_OPERAND (parm, 0);
- if (TREE_CODE (parm) == VAR_DECL && !DECL_ARTIFICIAL (parm))
- /* This isn't a temporary... */
- continue;
- if (TREE_CODE (parm) == PARM_DECL)
- /* .. nor is this... */
+ /* In case of component_ref, we need to capture the object of base
+ class as if it is temporary object. There are two possibilities:
+ (*base).field and base->field. */
+ while (TREE_CODE (parm) == COMPONENT_REF)
+ {
+ parm = TREE_OPERAND (parm, 0);
+ if (TREE_CODE (parm) == INDIRECT_REF)
+ parm = TREE_OPERAND (parm, 0);
+ parm = STRIP_NOPS (parm);
+ }
+
+ /* This isn't a temporary or argument. */
+ if ((TREE_CODE (parm) == VAR_DECL && !DECL_ARTIFICIAL (parm))
+ || TREE_CODE (parm) == PARM_DECL
+ || TREE_CODE (parm) == NON_LVALUE_EXPR)
continue;
if (TREE_CODE (parm) == TARGET_EXPR)
diff --git
a/gcc/testsuite/g++.dg/coroutines/torture/co-await-15-capture-comp-ref.C
b/gcc/testsuite/g++.dg/coroutines/torture/co-await-15-capture-comp-ref.C
new file mode 100644
index 00000000000..93a43fbd298
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-15-capture-comp-ref.C
@@ -0,0 +1,99 @@
+// { dg-do run }
+
+#include "../coro.h"
+
+class resumable {
+public:
+ struct promise_type;
+ using coro_handle = std::coroutine_handle<promise_type>;
+ resumable(coro_handle handle) : handle_(handle) { }
+ resumable(resumable&) = delete;
+ resumable(resumable&&) = delete;
+ ~resumable() { handle_.destroy(); }
+ coro_handle handle_;
+};
+
+struct resumable::promise_type {
+ using coro_handle = std::coroutine_handle<promise_type>;
+ int used;
+ auto get_return_object() {
+ return coro_handle::from_promise(*this);
+ }
+ auto initial_suspend() { return std::suspend_never(); }
+ auto final_suspend() { return std::suspend_always(); }
+ void return_value(int x) {used = x;}
+ void unhandled_exception() {}
+
+ struct TestAwaiter {
+ int recent_test;
+ TestAwaiter(int test) : recent_test{test} {}
+ bool await_ready() { return false; }
+ void await_suspend(std::coroutine_handle<promise_type>) {}
+ int await_resume() {
+ return recent_test;
+ }
+ auto operator co_await() {
+ return *this;
+ }
+ };
+
+ struct TestAwaiterCH :TestAwaiter {
+ TestAwaiterCH(int test) : TestAwaiter(test) {};
+ };
+
+ struct TestAwaiterCHCH :TestAwaiterCH {
+ TestAwaiterCHCH(int test) : TestAwaiterCH(test) {};
+
+ resumable foo(){
+ int x = co_await *this;
+ co_return x;
+ }
+ };
+};
+
+struct TestP {
+ resumable::promise_type::TestAwaiterCHCH tp =
resumable::promise_type::TestAwaiterCHCH(6);
+};
+
+resumable foo1(int t){
+ int x = co_await resumable::promise_type::TestAwaiterCH(t);
+ co_return x;
+}
+
+resumable foo2(){
+ struct TestP TP;
+ int x = co_await TP.tp;
+ co_return x;
+}
+
+resumable foo3(){
+ int x = co_await TestP{}.tp;
+ co_return x;
+}
+
+int main(){
+ auto t = resumable::promise_type::TestAwaiterCHCH(4);
+ resumable res = t.foo();
+ while (!res.handle_.done())
+ res.handle_.resume();
+ if (res.handle_.promise().used != 4)
+ abort();
+
+ resumable res1 = foo1(5);
+ while (!res1.handle_.done())
+ res1.handle_.resume();
+ if (res1.handle_.promise().used != 5)
+ abort();
+
+ resumable res2 = foo2();
+ while (!res2.handle_.done())
+ res2.handle_.resume();
+ if (res2.handle_.promise().used != 6)
+ abort();
+
+ resumable res3 = foo2();
+ while (!res3.handle_.done())
+ res3.handle_.resume();
+ if (res3.handle_.promise().used != 6)
+ abort();
+}
--
2.19.1.3.ge56e4f7