]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++, coroutines: Improve diagnostics for awaiter/promise.
authorIain Sandoe <iain@sandoe.co.uk>
Thu, 29 May 2025 15:50:44 +0000 (16:50 +0100)
committerIain Sandoe <iain@sandoe.co.uk>
Sat, 14 Jun 2025 10:40:55 +0000 (11:40 +0100)
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>
gcc/cp/coroutines.cc
gcc/cp/coroutines.h
gcc/testsuite/g++.dg/coroutines/coro-missing-final-suspend.C
gcc/testsuite/g++.dg/coroutines/coro1-missing-await-method.C
gcc/testsuite/g++.dg/coroutines/pr104051-0.C [moved from gcc/testsuite/g++.dg/coroutines/pr104051.C with 89% similarity]
gcc/testsuite/g++.dg/coroutines/pr104051-1.C [new file with mode: 0644]

index 1fbdee1b4f63606b0c71710f4c4ebab39bda39b9..4057a0762baf643c84f115995d12d83470573391 100644 (file)
@@ -1277,8 +1277,14 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
 
   if (TREE_CODE (o_type) != RECORD_TYPE)
     {
-      error_at (loc, "awaitable type %qT is not a structure",
-               o_type);
+      if (suspend_kind == FINAL_SUSPEND_POINT)
+       error_at (loc, "%qs awaitable type %qT is not a structure",
+                 "final_suspend()", o_type);
+      else if (suspend_kind == INITIAL_SUSPEND_POINT)
+       error_at (loc, "%qs awaitable type %qT is not a structure",
+                 "initial_suspend()", o_type);
+      else
+       error_at (loc, "awaitable type %qT is not a structure", o_type);
       return error_mark_node;
     }
 
@@ -4356,7 +4362,6 @@ cp_coroutine_transform::wrap_original_function_body ()
   /* Wrap the function body in a try {} catch (...) {} block, if exceptions
      are enabled.  */
   tree var_list = NULL_TREE;
-  tree initial_await = build_init_or_final_await (fn_start, false);
 
   /* [stmt.return.coroutine] / 3
      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 ()
   zero_resume = build2_loc (loc, MODIFY_EXPR, act_des_fn_ptr_type,
                            resume_fn_ptr, zero_resume);
   finish_expr_stmt (zero_resume);
-  finish_expr_stmt (build_init_or_final_await (fn_start, true));
+  finish_expr_stmt (final_await);
+
   BIND_EXPR_BODY (update_body) = pop_stmt_list (BIND_EXPR_BODY (update_body));
   BIND_EXPR_VARS (update_body) = nreverse (var_list);
   BLOCK_VARS (top_block) = BIND_EXPR_VARS (update_body);
@@ -5207,9 +5213,10 @@ cp_coroutine_transform::cp_coroutine_transform (tree _orig_fn, bool _inl)
       }
 
     /* We don't have the locus of the opening brace - it's filled in later (and
-       there doesn't really seem to be any easy way to get at it).
-       The closing brace is assumed to be input_location.  */
+       there doesn't really seem to be any easy way to get at it).  */
     fn_start = DECL_SOURCE_LOCATION (orig_fn_decl);
+    /* The closing brace is assumed to be input_location.  */
+    fn_end = input_location;
 
     /* Build types we need.  */
     tree fr_name = get_fn_local_identifier (orig_fn_decl, "Frame");
@@ -5288,6 +5295,16 @@ cp_coroutine_transform::apply_transforms ()
     = coro_build_actor_or_destroy_function (orig_fn_decl, act_des_fn_type,
                                            frame_ptr_type, false);
 
+  /* Avoid repeating diagnostics about promise or awaiter fails.  */
+  if (!seen_error ())
+    {
+      iloc_sentinel stable_input_loc (fn_start);
+      initial_await = build_init_or_final_await (fn_start, false);
+      input_location = fn_end;
+      if (initial_await && initial_await != error_mark_node)
+       final_await = build_init_or_final_await (fn_end, true);
+    }
+
   /* Transform the function body as per [dcl.fct.def.coroutine] / 5.  */
   wrap_original_function_body ();
 
index 55caa6e61e36db05e97957278b0c9d7e09c0a2c2..919dc9ab06b6efb09d04b32a1374650e771ecd4b 100644 (file)
@@ -102,6 +102,7 @@ private:
   tree orig_fn_decl;            /* The original function decl.  */
   tree orig_fn_body = NULL_TREE; /* The original function body.  */
   location_t fn_start = UNKNOWN_LOCATION;
+  location_t fn_end = UNKNOWN_LOCATION;
   tree resumer = error_mark_node;
   tree destroyer = error_mark_node;
   tree coroutine_body = NULL_TREE;
@@ -126,6 +127,9 @@ private:
   bool inline_p = false;
   bool valid_coroutine = false;
 
+  tree initial_await = error_mark_node;
+  tree final_await = error_mark_node;
+
   void analyze_fn_parms ();
   void wrap_original_function_body ();
   bool build_ramp_function ();
index 6a0878c1269afd7616f1f60c26216888864a3c67..b2522311a495f375edb1c3cd43ac7cc716d7e539 100644 (file)
@@ -7,10 +7,10 @@
 #include "coro1-ret-int-yield-int.h"
 
 coro1
-my_coro () // { dg-error {no member named 'final_suspend' in} }
+my_coro ()
 {
   co_return 0;
-}
+} // { dg-error {no member named 'final_suspend' in} }
 
 // check we have not messed up continuation of the compilation.
 template <class... Args>
index c1869e0654c31e981808460abe5d47578ea37834..c6a3188ee358f7403e46e0ff3b8a9642bdc16169 100644 (file)
@@ -7,7 +7,7 @@
 #include "coro1-ret-int-yield-int.h"
 
 coro1
-bar0 () // { dg-error {no member named 'await_suspend' in 'coro1::suspend_always_prt'} }
+bar0 ()
 {
   co_await coro1::suspend_never_prt{}; // { dg-error {no member named 'await_ready' in 'coro1::suspend_never_prt'} }
   co_yield 5; // { dg-error {no member named 'await_suspend' in 'coro1::suspend_always_prt'} }
similarity index 89%
rename from gcc/testsuite/g++.dg/coroutines/pr104051.C
rename to gcc/testsuite/g++.dg/coroutines/pr104051-0.C
index f77a915af7457209454f280ab371f185c267d607..bf878b2acbb82dcee495793c7e2173f1d75c7794 100644 (file)
@@ -24,7 +24,7 @@ template <typename T> struct task {
   std::coroutine_handle<> await_suspend(std::coroutine_handle<>);
   T await_resume();
 };
-task<std::vector<int>> foo() { // { dg-error {awaitable type 'bool' is not a structure} }
+task<std::vector<int>> foo() {
   while ((co_await foo()).empty())
     ;
-}
+} // { 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.C
new file mode 100644 (file)
index 0000000..35b5e2c
--- /dev/null
@@ -0,0 +1,23 @@
+// { dg-additional-options "-fsyntax-only" }
+// { dg-skip-if "requires hosted libstdc++ for vector" { ! hostedlib } }
+#include <coroutine>
+template <typename>
+struct promise {
+  auto get_return_object() {
+    return std::coroutine_handle<promise>::from_promise(*this);
+  }
+  auto initial_suspend() { return 42.0; }
+  auto final_suspend() noexcept { return true; }
+  void unhandled_exception();
+  void return_void ();
+};
+template <typename T> struct task {
+  using promise_type = promise<T>;
+  task(std::coroutine_handle<promise<T>>);
+  bool await_ready();
+  std::coroutine_handle<> await_suspend(std::coroutine_handle<>);
+  T await_resume();
+};
+task<double> foo() { // { dg-error {'initial_suspend\(\)' awaitable type 'double' is not a structure} }
+  co_return;
+}