gcc - GNU Compiler Collection (original) (raw)

author Iain Sandoe iain@sandoe.co.uk 2025-05-29 16:50:44 +0100
committer Iain Sandoe iain@sandoe.co.uk 2025-06-14 11:40:55 +0100
commit 4c014a9db521b24bd900eb9a6ac70988322a57da (patch)
tree 9532707f32caa59e15caa8efe82b60ecf0abef09
parent Daily bump. (diff)

c++, coroutines: Improve diagnostics for awaiter/promise.HEADtrunkmaster

At present, we can issue diagnostics about missing or malformed awaiter or promise methods when we encounter their uses in the body of a user's function. We might then re-issue the same diagnostics when processing the initial or final await expressions. This change avoids such duplication, and also attempts to identify issues with the initial or final expressions specifically since diagnostics for those do not have any useful line number. gcc/cp/ChangeLog: * coroutines.cc (build_co_await): Identify diagnostics for initial and final await expressions. (cp_coroutine_transform::wrap_original_function_body): Do not handle initial and final await expressions here ... (cp_coroutine_transform::apply_transforms): ... handle them here and avoid duplicate diagnostics. * coroutines.h: Declare inital and final await expressions in the transform class. Save the function closing brace location. gcc/testsuite/ChangeLog: * g++.dg/coroutines/coro1-missing-await-method.C: Adjust for improved diagnostics. * g++.dg/coroutines/coro-missing-final-suspend.C: Likewise. * g++.dg/coroutines/pr104051.C: Move to... * g++.dg/coroutines/pr104051-0.C: ...here. * g++.dg/coroutines/pr104051-1.C: New test. Signed-off-by: Iain Sandoe iain@sandoe.co.uk

-rw-r--r-- gcc/cp/coroutines.cc 29
-rw-r--r-- gcc/cp/coroutines.h 4
-rw-r--r-- gcc/testsuite/g++.dg/coroutines/coro-missing-final-suspend.C 4
-rw-r--r-- gcc/testsuite/g++.dg/coroutines/coro1-missing-await-method.C 2
-rw-r--r-- gcc/testsuite/g++.dg/coroutines/pr104051-0.C (renamed from gcc/testsuite/g++.dg/coroutines/pr104051.C) 4
-rw-r--r-- gcc/testsuite/g++.dg/coroutines/pr104051-1.C 23

6 files changed, 55 insertions, 11 deletions

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.ccindex 1fbdee1b4f63..4057a0762baf 100644--- a/gcc/cp/coroutines.cc+++ b/gcc/cp/coroutines.cc
@@ -1277,8 +1277,14 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
1277 1277
1278 if (TREE_CODE (o_type) != RECORD_TYPE) 1278 if (TREE_CODE (o_type) != RECORD_TYPE)
1279 { 1279 {
1280 error_at (loc, "awaitable type %qT is not a structure", 1280 if (suspend_kind == FINAL_SUSPEND_POINT)
1281 o_type); 1281 error_at (loc, "%qs awaitable type %qT is not a structure",
1282 "final_suspend()", o_type);
1283 else if (suspend_kind == INITIAL_SUSPEND_POINT)
1284 error_at (loc, "%qs awaitable type %qT is not a structure",
1285 "initial_suspend()", o_type);
1286 else
1287 error_at (loc, "awaitable type %qT is not a structure", o_type);
1282 return error_mark_node; 1288 return error_mark_node;
1283 } 1289 }
1284 1290
@@ -4356,7 +4362,6 @@ cp_coroutine_transform::wrap_original_function_body ()
4356 /* Wrap the function body in a try {} catch (...) {} block, if exceptions 4362 /* Wrap the function body in a try {} catch (...) {} block, if exceptions
4357 are enabled. */ 4363 are enabled. */
4358 tree var_list = NULL_TREE; 4364 tree var_list = NULL_TREE;
4359 tree initial_await = build_init_or_final_await (fn_start, false);
4360 4365
4361 /* [stmt.return.coroutine] / 3 4366 /* [stmt.return.coroutine] / 3
4362 If p.return_void() is a valid expression, flowing off the end of a 4367 If p.return_void() is a valid expression, flowing off the end of a
@@ -4550,7 +4555,8 @@ cp_coroutine_transform::wrap_original_function_body ()
4550 zero_resume = build2_loc (loc, MODIFY_EXPR, act_des_fn_ptr_type, 4555 zero_resume = build2_loc (loc, MODIFY_EXPR, act_des_fn_ptr_type,
4551 resume_fn_ptr, zero_resume); 4556 resume_fn_ptr, zero_resume);
4552 finish_expr_stmt (zero_resume); 4557 finish_expr_stmt (zero_resume);
4553 finish_expr_stmt (build_init_or_final_await (fn_start, true)); 4558 finish_expr_stmt (final_await);
4559
4554 BIND_EXPR_BODY (update_body) = pop_stmt_list (BIND_EXPR_BODY (update_body)); 4560 BIND_EXPR_BODY (update_body) = pop_stmt_list (BIND_EXPR_BODY (update_body));
4555 BIND_EXPR_VARS (update_body) = nreverse (var_list); 4561 BIND_EXPR_VARS (update_body) = nreverse (var_list);
4556 BLOCK_VARS (top_block) = BIND_EXPR_VARS (update_body); 4562 BLOCK_VARS (top_block) = BIND_EXPR_VARS (update_body);
@@ -5207,9 +5213,10 @@ cp_coroutine_transform::cp_coroutine_transform (tree _orig_fn, bool _inl)
5207 } 5213 }
5208 5214
5209 /* We don't have the locus of the opening brace - it's filled in later (and 5215 /* We don't have the locus of the opening brace - it's filled in later (and
5210 there doesn't really seem to be any easy way to get at it). 5216 there doesn't really seem to be any easy way to get at it). */
5211 The closing brace is assumed to be input_location. */
5212 fn_start = DECL_SOURCE_LOCATION (orig_fn_decl); 5217 fn_start = DECL_SOURCE_LOCATION (orig_fn_decl);
5218 /* The closing brace is assumed to be input_location. */
5219 fn_end = input_location;
5213 5220
5214 /* Build types we need. */ 5221 /* Build types we need. */
5215 tree fr_name = get_fn_local_identifier (orig_fn_decl, "Frame"); 5222 tree fr_name = get_fn_local_identifier (orig_fn_decl, "Frame");
@@ -5288,6 +5295,16 @@ cp_coroutine_transform::apply_transforms ()
5288 = coro_build_actor_or_destroy_function (orig_fn_decl, act_des_fn_type, 5295 = coro_build_actor_or_destroy_function (orig_fn_decl, act_des_fn_type,
5289 frame_ptr_type, false); 5296 frame_ptr_type, false);
5290 5297
5298 /* Avoid repeating diagnostics about promise or awaiter fails. */
5299 if (!seen_error ())
5300 {
5301 iloc_sentinel stable_input_loc (fn_start);
5302 initial_await = build_init_or_final_await (fn_start, false);
5303 input_location = fn_end;
5304 if (initial_await && initial_await != error_mark_node)
5305 final_await = build_init_or_final_await (fn_end, true);
5306 }
5307
5291 /* Transform the function body as per [dcl.fct.def.coroutine] / 5. */ 5308 /* Transform the function body as per [dcl.fct.def.coroutine] / 5. */
5292 wrap_original_function_body (); 5309 wrap_original_function_body ();
5293 5310
diff --git a/gcc/cp/coroutines.h b/gcc/cp/coroutines.hindex 55caa6e61e36..919dc9ab06b6 100644--- a/gcc/cp/coroutines.h+++ b/gcc/cp/coroutines.h
@@ -102,6 +102,7 @@ private:
102 tree orig_fn_decl; /* The original function decl. */ 102 tree orig_fn_decl; /* The original function decl. */
103 tree orig_fn_body = NULL_TREE; /* The original function body. */ 103 tree orig_fn_body = NULL_TREE; /* The original function body. */
104 location_t fn_start = UNKNOWN_LOCATION; 104 location_t fn_start = UNKNOWN_LOCATION;
105 location_t fn_end = UNKNOWN_LOCATION;
105 tree resumer = error_mark_node; 106 tree resumer = error_mark_node;
106 tree destroyer = error_mark_node; 107 tree destroyer = error_mark_node;
107 tree coroutine_body = NULL_TREE; 108 tree coroutine_body = NULL_TREE;
@@ -126,6 +127,9 @@ private:
126 bool inline_p = false; 127 bool inline_p = false;
127 bool valid_coroutine = false; 128 bool valid_coroutine = false;
128 129
130 tree initial_await = error_mark_node;
131 tree final_await = error_mark_node;
132
129 void analyze_fn_parms (); 133 void analyze_fn_parms ();
130 void wrap_original_function_body (); 134 void wrap_original_function_body ();
131 bool build_ramp_function (); 135 bool build_ramp_function ();
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-final-suspend.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-final-suspend.Cindex 6a0878c1269a..b2522311a495 100644--- a/gcc/testsuite/g++.dg/coroutines/coro-missing-final-suspend.C+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-final-suspend.C
@@ -7,10 +7,10 @@
7 #include "coro1-ret-int-yield-int.h" 7 #include "coro1-ret-int-yield-int.h"
8 8
9 coro1 9 coro1
10 my_coro () // { dg-error {no member named 'final_suspend' in} } 10 my_coro ()
11 { 11 {
12 co_return 0; 12 co_return 0;
13 } 13 } // { dg-error {no member named 'final_suspend' in} }
14 14
15 // check we have not messed up continuation of the compilation. 15 // check we have not messed up continuation of the compilation.
16 template <class... Args> 16 template <class... Args>
diff --git a/gcc/testsuite/g++.dg/coroutines/coro1-missing-await-method.C b/gcc/testsuite/g++.dg/coroutines/coro1-missing-await-method.Cindex c1869e0654c3..c6a3188ee358 100644--- a/gcc/testsuite/g++.dg/coroutines/coro1-missing-await-method.C+++ b/gcc/testsuite/g++.dg/coroutines/coro1-missing-await-method.C
@@ -7,7 +7,7 @@
7 #include "coro1-ret-int-yield-int.h" 7 #include "coro1-ret-int-yield-int.h"
8 8
9 coro1 9 coro1
10 bar0 () // { dg-error {no member named 'await_suspend' in 'coro1::suspend_always_prt'} } 10 bar0 ()
11 { 11 {
12 co_await coro1::suspend_never_prt{}; // { dg-error {no member named 'await_ready' in 'coro1::suspend_never_prt'} } 12 co_await coro1::suspend_never_prt{}; // { dg-error {no member named 'await_ready' in 'coro1::suspend_never_prt'} }
13 co_yield 5; // { dg-error {no member named 'await_suspend' in 'coro1::suspend_always_prt'} } 13 co_yield 5; // { dg-error {no member named 'await_suspend' in 'coro1::suspend_always_prt'} }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr104051.C b/gcc/testsuite/g++.dg/coroutines/pr104051-0.Cindex f77a915af745..bf878b2acbb8 100644--- a/gcc/testsuite/g++.dg/coroutines/pr104051.C+++ b/gcc/testsuite/g++.dg/coroutines/pr104051-0.C
@@ -24,7 +24,7 @@ template struct task {
24 std::coroutine_handle<> await_suspend(std::coroutine_handle<>); 24 std::coroutine_handle<> await_suspend(std::coroutine_handle<>);
25 T await_resume(); 25 T await_resume();
26 }; 26 };
27 task<std::vector> foo() { // { dg-error {awaitable type 'bool' is not a structure} } 27 task<std::vector> foo() {
28 while ((co_await foo()).empty()) 28 while ((co_await foo()).empty())
29 ; 29 ;
30 } 30 } // { dg-error {'final_suspend\(\)' awaitable type 'bool' is not a structure} }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr104051-1.C b/gcc/testsuite/g++.dg/coroutines/pr104051-1.Cnew file mode 100644index 000000000000..35b5e2c700d3--- /dev/null+++ b/gcc/testsuite/g++.dg/coroutines/pr104051-1.C
@@ -0,0 +1,23 @@
1 // { dg-additional-options "-fsyntax-only" }
2 // { dg-skip-if "requires hosted libstdc++ for vector" { ! hostedlib } }
3 #include
4 template
5 struct promise {
6 auto get_return_object() {
7 return std::coroutine_handle::from_promise(*this);
8 }
9 auto initial_suspend() { return 42.0; }
10 auto final_suspend() noexcept { return true; }
11 void unhandled_exception();
12 void return_void ();
13 };
14 template struct task {
15 using promise_type = promise;
16 task(std::coroutine_handle<promise>);
17 bool await_ready();
18 std::coroutine_handle<> await_suspend(std::coroutine_handle<>);
19 T await_resume();
20 };
21 task foo() { // { dg-error {'initial_suspend\(\)' awaitable type 'double' is not a structure} }
22 co_return;
23 }