gcc/cp/ChangeLog:
* coroutines.cc (co_await_get_resume_call): Return a tree
directly, rather than a tree pointer.
* cp-tree.h (co_await_get_resume_call): Adjust signature
accordingly.
* cvt.cc (convert_to_void): Do not alter CO_AWAIT_EXPRs when
discarding them. Simplify handling implicit INDIRECT_REFs.
gcc/testsuite/ChangeLog:
* g++.dg/coroutines/nodiscard-1.C: New test.
---
gcc/cp/coroutines.cc | 4 +-
gcc/cp/cp-tree.h | 2 +-
gcc/cp/cvt.cc | 112 +++++++++---------
gcc/testsuite/g++.dg/coroutines/nodiscard-1.C | 77 ++++++++++++
4 files changed, 137 insertions(+), 58 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/coroutines/nodiscard-1.C
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index f9129b5f988b..33878b7a876b 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -867,14 +867,14 @@ coro_get_destroy_function (tree decl)
/* Given a CO_AWAIT_EXPR AWAIT_EXPR, return its resume call. */
-tree*
+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);
+ return TREE_VEC_ELT (vec, 2);
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 87e3da49ea97..2aad1e497338 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8780,7 +8780,7 @@ 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);
+extern tree co_await_get_resume_call (tree await_expr);
/* contracts.cc */
diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc
index df02b8faaf51..526937d36181 100644
--- a/gcc/cp/cvt.cc
+++ b/gcc/cp/cvt.cc
@@ -1272,54 +1272,15 @@ convert_to_void (tree expr, impl_conv_void implicit,
tsubst_flags_t complain)
complete_type (type);
int is_complete = COMPLETE_TYPE_P (type);
- /* Can't load the value if we don't know the type. */
- if (is_volatile && !is_complete)
- {
- if (complain & tf_warning)
- switch (implicit)
- {
- case ICV_CAST:
- warning_at (loc, 0, "conversion to void will not access "
- "object of incomplete type %qT", type);
- break;
- case ICV_SECOND_OF_COND:
- warning_at (loc, 0, "indirection will not access object of "
- "incomplete type %qT in second operand "
- "of conditional expression", type);
- break;
- case ICV_THIRD_OF_COND:
- warning_at (loc, 0, "indirection will not access object of "
- "incomplete type %qT in third operand "
- "of conditional expression", type);
- break;
- case ICV_RIGHT_OF_COMMA:
- warning_at (loc, 0, "indirection will not access object of "
- "incomplete type %qT in right operand of "
- "comma operator", type);
- break;
- case ICV_LEFT_OF_COMMA:
- warning_at (loc, 0, "indirection will not access object of "
- "incomplete type %qT in left operand of "
- "comma operator", type);
- break;
- case ICV_STATEMENT:
- warning_at (loc, 0, "indirection will not access object of "
- "incomplete type %qT in statement", type);
- break;
- case ICV_THIRD_IN_FOR:
- warning_at (loc, 0, "indirection will not access object of "
- "incomplete type %qT in for increment "
- "expression", type);
- break;
- default:
- gcc_unreachable ();
- }
- }
/* Don't load the value if this is an implicit dereference, or if
the type needs to be handled by ctors/dtors. */
- else if (is_volatile && is_reference)
+ if (is_reference)
{
- if (complain & tf_warning)
+ if (is_volatile && (complain & tf_warning)
+ /* A co_await expression, in its await_resume expression, also
+ contains an implicit dereference. As a result, we don't
+ need to warn about them here. */
+ && TREE_CODE (TREE_OPERAND (expr, 0)) != CO_AWAIT_EXPR)
switch (implicit)
{
case ICV_CAST:
@@ -1358,6 +1319,53 @@ convert_to_void (tree expr, impl_conv_void implicit,
tsubst_flags_t complain)
default:
gcc_unreachable ();
}
+
+ /* Since this was an implicit dereference, we should also act as if
+ it was never there. */
+ return convert_to_void (TREE_OPERAND (expr, 0), implicit, complain);
+ }
+ /* Can't load the value if we don't know the type. */
+ else if (is_volatile && !is_complete)
+ {
+ if (complain & tf_warning)
+ switch (implicit)
+ {
+ case ICV_CAST:
+ warning_at (loc, 0, "conversion to void will not access "
+ "object of incomplete type %qT", type);
+ break;
+ case ICV_SECOND_OF_COND:
+ warning_at (loc, 0, "indirection will not access object of "
+ "incomplete type %qT in second operand "
+ "of conditional expression", type);
+ break;
+ case ICV_THIRD_OF_COND:
+ warning_at (loc, 0, "indirection will not access object of "
+ "incomplete type %qT in third operand "
+ "of conditional expression", type);
+ break;
+ case ICV_RIGHT_OF_COMMA:
+ warning_at (loc, 0, "indirection will not access object of "
+ "incomplete type %qT in right operand of "
+ "comma operator", type);
+ break;
+ case ICV_LEFT_OF_COMMA:
+ warning_at (loc, 0, "indirection will not access object of "
+ "incomplete type %qT in left operand of "
+ "comma operator", type);
+ break;
+ case ICV_STATEMENT:
+ warning_at (loc, 0, "indirection will not access object of "
+ "incomplete type %qT in statement", type);
+ break;
+ case ICV_THIRD_IN_FOR:
+ warning_at (loc, 0, "indirection will not access object of "
+ "incomplete type %qT in for increment "
+ "expression", type);
+ break;
+ default:
+ gcc_unreachable ();
+ }
}
else if (is_volatile && TREE_ADDRESSABLE (type))
{
@@ -1403,7 +1411,7 @@ convert_to_void (tree expr, impl_conv_void implicit,
tsubst_flags_t complain)
gcc_unreachable ();
}
}
- if (is_reference || !is_volatile || !is_complete || TREE_ADDRESSABLE
(type))
+ if (!is_volatile || !is_complete || TREE_ADDRESSABLE (type))
{
/* Emit a warning (if enabled) when the "effect-less" INDIRECT_REF
operation is stripped off. Note that we don't warn about
@@ -1418,9 +1426,6 @@ convert_to_void (tree expr, impl_conv_void implicit,
tsubst_flags_t complain)
&& !is_reference)
warning_at (loc, OPT_Wunused_value, "value computed is not
used");
expr = TREE_OPERAND (expr, 0);
- if (TREE_CODE (expr) == CALL_EXPR
- && (complain & tf_warning))
- maybe_warn_nodiscard (expr, implicit);
}
break;
@@ -1503,12 +1508,9 @@ convert_to_void (tree expr, impl_conv_void implicit,
tsubst_flags_t complain)
break;
case CO_AWAIT_EXPR:
- {
- auto awr = co_await_get_resume_call (expr);
- if (awr && *awr)
- *awr = convert_to_void (*awr, implicit, complain);
- break;
- }
+ if (auto awr = co_await_get_resume_call (expr))
+ convert_to_void (awr, implicit, complain);
+ break;
default:;
}
diff --git a/gcc/testsuite/g++.dg/coroutines/nodiscard-1.C
b/gcc/testsuite/g++.dg/coroutines/nodiscard-1.C
new file mode 100644
index 000000000000..b4c67292fcb7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/nodiscard-1.C
@@ -0,0 +1,77 @@
+#include <coroutine>
+/* Make sure that we correctly warn for unused co_await results. */
+
+struct task
+{
+ struct promise_type
+ {
+ std::suspend_never initial_suspend () noexcept;
+ std::suspend_never final_suspend () noexcept;
+ void return_void ();
+ void unhandled_exception ();
+ task get_return_object ();
+ };
+};
+
+template<typename T>
+struct nodiscardable : std::suspend_never
+{ [[nodiscard]] T await_resume (); };
+
+template<typename T>
+struct discardable : std::suspend_never
+{ T await_resume (); };
+
+task
+thing ()
+{
+ co_await nodiscardable<int&>{}; /* { dg-warning "attribute 'nodiscard'" } */
+ co_await nodiscardable<int>{}; /* { dg-warning "attribute 'nodiscard'" } */
+
+ (void) co_await nodiscardable<int&>{};
+ (void) co_await nodiscardable<int>{};
+
+ co_await discardable<int&>{};
+ co_await discardable<volatile int&>{};
+ /* { dg-warning "implicit dereference will not access" "" { target *-*-* }
{.-1} } */
+}
+
+template<typename>
+task
+other_thing ()
+{
+ co_await nodiscardable<int&>{}; /* { dg-warning "attribute 'nodiscard'" } */
+ co_await nodiscardable<int>{}; /* { dg-warning "attribute 'nodiscard'" } */
+
+ (void) co_await nodiscardable<int&>{};
+ (void) co_await nodiscardable<int>{};
+
+ co_await discardable<int&>{};
+ co_await discardable<volatile int&>{};
+ /* { dg-warning "implicit dereference will not access" "" { target *-*-* }
{.-1} } */
+}
+
+void
+other_thing_caller ()
+{
+ other_thing <int> ();
+}
+
+task
+yet_another_thing (auto)
+{
+ co_await nodiscardable<int&>{}; /* { dg-warning "attribute 'nodiscard'" } */
+ co_await nodiscardable<int>{}; /* { dg-warning "attribute 'nodiscard'" } */
+
+ (void) co_await nodiscardable<int&>{};
+ (void) co_await nodiscardable<int>{};
+
+ co_await discardable<int&>{};
+ co_await discardable<volatile int&>{};
+ /* { dg-warning "implicit dereference will not access" "" { target *-*-* }
{.-1} } */
+}
+
+void
+yet_another_thing_caller ()
+{
+ yet_another_thing (1);
+}