diff options
author | Arsen Arsenović <arsen@aarsen.me> | 2024-07-22 15:49:20 +0200 |
---|---|---|
committer | Thomas Koenig <tkoenig@gcc.gnu.org> | 2024-07-28 19:05:57 +0200 |
commit | f3131a07e5095ddb07adbd75ddba546a5e9bcbde (patch) | |
tree | a052a9c75d8c2597100a35e958f01a9d1363ecb2 | |
parent | 3109e8c991762031b108ae2a022472b989cbb465 (diff) | |
download | gcc-f3131a07e5095ddb07adbd75ddba546a5e9bcbde.zip gcc-f3131a07e5095ddb07adbd75ddba546a5e9bcbde.tar.gz gcc-f3131a07e5095ddb07adbd75ddba546a5e9bcbde.tar.bz2 |
cp+coroutines: teach convert_to_void to diagnose discarded co_awaits
co_await expressions are nearly calls to Awaitable::await_resume, and,
as such, should inherit its nodiscard. A discarded co_await expression
should, hence, act as if its call to await_resume was discarded.
This patch teaches convert_to_void how to discard 'through' a
CO_AWAIT_EXPR. When we discard a CO_AWAIT_EXPR, we can also just discard
the await_resume() call conveniently embedded within it. This results
in a [[nodiscard]] diagnostic that the PR noted was missing.
gcc/cp/ChangeLog:
PR c++/110171
* coroutines.cc (co_await_get_resume_call): New function.
Returns the await_resume expression of a given co_await.
* cp-tree.h (co_await_get_resume_call): New function.
* cvt.cc (convert_to_void): Handle CO_AWAIT_EXPRs and call
maybe_warn_nodiscard on their resume exprs.
gcc/testsuite/ChangeLog:
PR c++/110171
* g++.dg/coroutines/pr110171-1.C: New test.
* g++.dg/coroutines/pr110171.C: New test.
-rw-r--r-- | gcc/cp/coroutines.cc | 15 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 3 | ||||
-rw-r--r-- | gcc/cp/cvt.cc | 8 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/coroutines/pr110171-1.C | 34 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/coroutines/pr110171.C | 32 |
5 files changed, 91 insertions, 1 deletions
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 4ddd986..2b16b48 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -596,6 +596,19 @@ coro_get_destroy_function (tree decl) return NULL_TREE; } +/* Given a CO_AWAIT_EXPR AWAIT_EXPR, return its resume call. */ + +tree* +co_await_get_resume_call (tree await_expr) +{ + gcc_checking_assert (TREE_CODE (await_expr) == CO_AWAIT_EXPR); + tree vec = TREE_OPERAND (await_expr, 3); + if (!vec) + return nullptr; + return &TREE_VEC_ELT (vec, 2); +} + + /* These functions assumes that the caller has verified that the state for the decl has been initialized, we try to minimize work here. */ @@ -4566,7 +4579,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) param_info *parm_i = param_uses->get (arg); if (parm_i->trivial_dtor) continue; - add_decl_expr (parm_i->guard_var);; + add_decl_expr (parm_i->guard_var); } add_decl_expr (coro_promise_live); add_decl_expr (coro_gro_live); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 0382467..7d50aac 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8768,6 +8768,9 @@ extern tree coro_get_actor_function (tree); extern tree coro_get_destroy_function (tree); extern tree coro_get_ramp_function (tree); +extern tree* co_await_get_resume_call (tree await_expr); + + /* contracts.cc */ extern tree make_postcondition_variable (cp_expr); extern tree make_postcondition_variable (cp_expr, tree); diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc index d95e01c..7b4bd8a 100644 --- a/gcc/cp/cvt.cc +++ b/gcc/cp/cvt.cc @@ -1502,6 +1502,14 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain) maybe_warn_nodiscard (expr, implicit); break; + case CO_AWAIT_EXPR: + { + auto awr = co_await_get_resume_call (expr); + if (awr && *awr) + *awr = convert_to_void (*awr, implicit, complain); + break; + } + default:; } expr = resolve_nondeduced_context (expr, complain); diff --git a/gcc/testsuite/g++.dg/coroutines/pr110171-1.C b/gcc/testsuite/g++.dg/coroutines/pr110171-1.C new file mode 100644 index 0000000..d8aff58 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr110171-1.C @@ -0,0 +1,34 @@ +// { dg-do compile } +#include <coroutine> + +struct must_check_result +{ + bool await_ready() { return false; } + void await_suspend(std::coroutine_handle<>) {} + [[nodiscard]] bool await_resume() { return {}; } +}; + +struct task {}; + +namespace std +{ + template<typename... Args> + struct coroutine_traits<task, Args...> + { + struct promise_type + { + task get_return_object() { return {}; } + suspend_always initial_suspend() noexcept { return {}; } + suspend_always final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() {} + }; + }; +} + +task example(auto) +{ + co_await must_check_result(); // { dg-warning "-Wunused-result" } +} + +void f() { example(1); } diff --git a/gcc/testsuite/g++.dg/coroutines/pr110171.C b/gcc/testsuite/g++.dg/coroutines/pr110171.C new file mode 100644 index 0000000..4b82e23 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr110171.C @@ -0,0 +1,32 @@ +// { dg-do compile } +#include <coroutine> + +struct must_check_result +{ + bool await_ready() { return false; } + void await_suspend(std::coroutine_handle<>) {} + [[nodiscard]] bool await_resume() { return {}; } +}; + +struct task {}; + +namespace std +{ + template<typename... Args> + struct coroutine_traits<task, Args...> + { + struct promise_type + { + task get_return_object() { return {}; } + suspend_always initial_suspend() noexcept { return {}; } + suspend_always final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() {} + }; + }; +} + +task example() +{ + co_await must_check_result(); // { dg-warning "-Wunused-result" } +} |