https://gcc.gnu.org/g:4c014a9db521b24bd900eb9a6ac70988322a57da

commit r16-1510-g4c014a9db521b24bd900eb9a6ac70988322a57da
Author: Iain Sandoe <i...@sandoe.co.uk>
Date:   Thu May 29 16:50:44 2025 +0100

    c++, coroutines: Improve diagnostics for awaiter/promise.
    
    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 <i...@sandoe.co.uk>

Diff:
---
 gcc/cp/coroutines.cc                               | 29 +++++++++++++++++-----
 gcc/cp/coroutines.h                                |  4 +++
 .../g++.dg/coroutines/coro-missing-final-suspend.C |  4 +--
 .../g++.dg/coroutines/coro1-missing-await-method.C |  2 +-
 .../g++.dg/coroutines/{pr104051.C => pr104051-0.C} |  4 +--
 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.cc
index 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,
 
   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 ();
 
diff --git a/gcc/cp/coroutines.h b/gcc/cp/coroutines.h
index 55caa6e61e36..919dc9ab06b6 100644
--- a/gcc/cp/coroutines.h
+++ b/gcc/cp/coroutines.h
@@ -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 ();
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-final-suspend.C 
b/gcc/testsuite/g++.dg/coroutines/coro-missing-final-suspend.C
index 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 @@
 #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>
diff --git a/gcc/testsuite/g++.dg/coroutines/coro1-missing-await-method.C 
b/gcc/testsuite/g++.dg/coroutines/coro1-missing-await-method.C
index 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 @@
 #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'} }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr104051.C 
b/gcc/testsuite/g++.dg/coroutines/pr104051-0.C
similarity index 89%
rename from gcc/testsuite/g++.dg/coroutines/pr104051.C
rename to gcc/testsuite/g++.dg/coroutines/pr104051-0.C
index 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 <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
index 000000000000..35b5e2c700d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr104051-1.C
@@ -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;
+}

Reply via email to