On 6/9/25 4:09 PM, Iain Sandoe wrote:
Hi Jason,
As discussed in the PR, fold the expression in the coroutine lowering.
tested on x86_64-darwin, powerpc64le-linux, OK for trunk?
thanks
Iain
--- 8< ---
Since the folding of this builtin happens after the main coroutine FE
lowering, we need to account for await expressions in that lowering.
Since these expressions have a property of being not evaluated, but do
not have the full constraints of an unevaluatated context, we want to
apply the checks and then remove the await expressions so that they no
longer participate in the analysis and lowering.
When a builtin_constant_p call is encountered, and the operand contains
any await expression, we check to see if the operand can be a constant
and replace the call with its result.
PR c++/116775
gcc/cp/ChangeLog:
* coroutines.cc (analyze_expression_awaits): When we see
a builtin_constant_p call, and that contains one or more
await expressions, then replace the call with its result
and discard the unevaluated operand.
gcc/testsuite/ChangeLog:
* g++.dg/coroutines/pr116775.C: New test.
Signed-off-by: Iain Sandoe <i...@sandoe.co.uk>
---
gcc/cp/coroutines.cc | 23 +++++++-
gcc/testsuite/g++.dg/coroutines/pr116775.C | 68 ++++++++++++++++++++++
2 files changed, 90 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/g++.dg/coroutines/pr116775.C
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 4d6ec171cd5..bbdc33abc0e 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -3551,7 +3551,8 @@ maybe_promote_temps (tree *stmt, void *d)
return cp_walk_tree (stmt, register_awaits, d, &visited);
}
-/* Lightweight callback to determine two key factors:
+/* Relatively lightweight callback to do initial assessment:
+ 0) Rewrite some await expressions.
1) If the statement/expression contains any await expressions.
2) If the statement/expression potentially requires a re-write to handle
TRUTH_{AND,OR}IF_EXPRs since, in most cases, they will need expansion
@@ -3568,6 +3569,26 @@ analyze_expression_awaits (tree *stmt, int *do_subtree,
void *d)
switch (TREE_CODE (*stmt))
{
default: return NULL_TREE;
+ case CALL_EXPR:
+ {
+ tree fn = cp_get_callee_fndecl_nofold (*stmt);
+ /* Special-cases where we want to re-write await expressions to some
+ other value before they are otherwise processed. */
+ if (fn && DECL_IS_BUILTIN_CONSTANT_P (fn))
+ {
+ gcc_checking_assert (call_expr_nargs (*stmt) == 1);
+ tree expr = CALL_EXPR_ARG (*stmt, 0);
+ if (cp_walk_tree (&expr, find_any_await, nullptr, NULL))
+ {
+ if (maybe_constant_value (expr) == expr)
The usual pattern is to check TREE_CONSTANT on the result of
maybe_constant_value. OK with that change.
+ *stmt = integer_zero_node;
+ else
+ *stmt = integer_one_node;
+ }
+ *do_subtree = 0;
+ }
+ }
+ break;
case CO_YIELD_EXPR:
/* co_yield is syntactic sugar, re-write it to co_await. */
*stmt = TREE_OPERAND (*stmt, 1);
diff --git a/gcc/testsuite/g++.dg/coroutines/pr116775.C
b/gcc/testsuite/g++.dg/coroutines/pr116775.C
new file mode 100644
index 00000000000..981e27af27b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr116775.C
@@ -0,0 +1,68 @@
+// { dg-additional-options "-fsyntax-only" }
+// PR116775
+#include <coroutine>
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF(...)
+#else
+#include <cstdio>
+# define PRINT(X) puts(X)
+# define PRINTF(...) printf(__VA_ARGS__)
+#endif
+
+struct awaitable {
+ awaitable(int n) : delay{n} {}
+
+ constexpr bool await_ready() const noexcept { return false; }
+ auto await_suspend(std::coroutine_handle<> h) const {
+ __builtin_trap ();
+ return false;
+ }
+ int await_resume() const noexcept {
+ return delay;
+ }
+
+ int delay;
+};
+
+struct Task {
+ struct promise_type {
+ promise_type() = default;
+ Task get_return_object() { return {}; }
+ std::suspend_never initial_suspend() { return {}; }
+ std::suspend_always final_suspend() noexcept { return {}; }
+ void unhandled_exception() {}
+ void return_void () {}
+ awaitable yield_value (int v) { return {v}; }
+ };
+};
+
+Task foo() noexcept {
+ if (__builtin_constant_p (true ? 1 : co_await awaitable{10}))
+ PRINT ("const OK");
+ else
+ {
+ PRINT ("failed : should be const");
+ __builtin_abort ();
+ }
+ if (__builtin_constant_p (false ? 1 : co_await awaitable{15}))
+ {
+ PRINT ("failed : should not be const");
+ __builtin_abort ();
+ }
+ else
+ PRINT ("not const, OK");
+ if (__builtin_constant_p (5 == (co_yield 42)))
+ {
+ PRINT ("failed : should not be const");
+ __builtin_abort ();
+ }
+ else
+ PRINT ("not const, OK");
+ co_return;
+}
+
+//call foo
+int main() {
+ foo();
+}