https://gcc.gnu.org/g:de03ef6337b0a368d61c74b790313b4216c7ed6e
commit r15-3922-gde03ef6337b0a368d61c74b790313b4216c7ed6e Author: Arsen Arsenović <ar...@aarsen.me> Date: Fri Sep 20 13:13:02 2024 +0200 c++: simplify handling implicit INDIRECT_REF and co_await in convert_to_void convert_to_void has, so far, when converting a co_await expression to void altered the await_resume expression of a co_await so that it is also converted to void. This meant that the type of the await_resume expression, which is also supposed to be the type of the whole co_await expression, was not the same as the type of the CO_AWAIT_EXPR tree. While this has not caused problems so far, it is unexpected, I think. Also, convert_to_void had a special case when an INDIRECT_REF wrapped a CALL_EXPR. In this case, we also diagnosed maybe_warn_nodiscard. This was a duplication of logic related to converting call expressions to void. Instead, we can generalize a bit, and rather discard the expression that was implicitly dereferenced instead. This patch changes the diagnostic of: void f(struct S* x) { static_cast<volatile S&>(*x); } ... from: warning: indirection will not access object of incomplete type 'volatile S' in statement ... to: warning: implicit dereference will not access object of type ‘volatile S’ in statement ... but should have no impact in other cases. 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. Diff: --- gcc/cp/coroutines.cc | 4 +- gcc/cp/cp-tree.h | 2 +- gcc/cp/cvt.cc | 102 +++++++++++++------------- gcc/testsuite/g++.dg/coroutines/nodiscard-1.C | 77 +++++++++++++++++++ 4 files changed, 132 insertions(+), 53 deletions(-) diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 50904e0d004e..8e4c55a800e4 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -730,14 +730,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 7c438eca16d6..39c065eecf6d 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8782,7 +8782,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,88 +1272,96 @@ 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) + /* Don't load the value if this is an implicit dereference, or if + the type needs to be handled by ctors/dtors. */ + 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: warning_at (loc, 0, "conversion to void will not access " - "object of incomplete type %qT", type); + "object of 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); + warning_at (loc, 0, "implicit dereference will not access " + "object of 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); + warning_at (loc, 0, "implicit dereference will not access " + "object of 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 " + warning_at (loc, 0, "implicit dereference will not access " + "object of 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); + warning_at (loc, 0, "implicit dereference will not access " + "object of 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); + warning_at (loc, 0, "implicit dereference will not access " + "object of 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); + warning_at (loc, 0, "implicit dereference will not access " + "object of type %qT in for increment expression", + type); break; 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); } - /* 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) + /* 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 type %qT", type); + "object of incomplete type %qT", type); break; case ICV_SECOND_OF_COND: - warning_at (loc, 0, "implicit dereference will not access " - "object of type %qT in second operand of " - "conditional expression", type); + 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, "implicit dereference will not access " - "object of type %qT in third operand of " - "conditional expression", type); + 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, "implicit dereference will not access " - "object of type %qT in right operand of " + 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, "implicit dereference will not access " - "object of type %qT in left operand of comma " - "operator", type); + 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, "implicit dereference will not access " - "object of type %qT in statement", type); + 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, "implicit dereference will not access " - "object of type %qT in for increment expression", - type); + warning_at (loc, 0, "indirection will not access object of " + "incomplete type %qT in for increment " + "expression", type); break; default: gcc_unreachable (); @@ -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); +}