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 | } |