On 8/7/24 7:31 PM, Arsen Arsenović wrote:
Enlargening the function-specific data block is not great.

Indeed, I think it would be better to search DECL_SAVED_TREE for a RETURN_STMT once we've decided to give an error.

I've
considered changing the location of RETURN_STMT expressions to cover
everything from the return expression to input_location after parsing
the returned expr.  The result of that is:

test.cc:38:3: error: a ‘return’ statement is not allowed in coroutine; did you 
mean ‘co_return’?
    38 |   return {};
       |   ^~~~~~~~~
test.cc:37:3: note: function was made a coroutine here
    37 |   co_return;
       |   ^~~~~~~~~

... so, not bad, but I'm not sure how intrusive such a change would be
(haven't tried the testsuite).  The current patch produces:

test.cc:36:3: error: a ‘return’ statement is not allowed in coroutine; did you 
mean ‘co_return’?
    36 |   return {};
       |   ^~~~~~
test.cc:35:3: note: function was made a coroutine here
    35 |   co_return;
       |   ^~~~~~~~~


Is there a better location to use here or is the current (latter) one
OK?

The latter seems fine.

I haven't managed to found a nicer existing one.  We also can't
stash it in coroutine_info because a function might not have that at
time we parse a return.

Tested on x86_64-pc-linux-gnu.

Have a lovely evening.
---------- >8 ----------
We now point out why a function is a coroutine.

gcc/cp/ChangeLog:

        * coroutines.cc (coro_function_valid_p): Change how we diagnose
        returning coroutines.
        * cp-tree.h (struct language_function): Add first_return_loc
        field.  Tracks the location of the first return encountered
        during parsing.
        (current_function_first_return_loc): New macro.  Expands to the
        current functions' first_return_loc.
        * parser.cc (cp_parser_jump_statement): If parsing a RID_RETURN,
        save its location to current_function_first_return_loc.

gcc/testsuite/ChangeLog:

        * g++.dg/coroutines/co-return-syntax-08-bad-return.C: Update to
        match new diagnostic.
---
  gcc/cp/coroutines.cc                          | 14 +++--
  gcc/cp/cp-tree.h                              |  6 +++
  gcc/cp/parser.cc                              |  4 ++
  .../co-return-syntax-08-bad-return.C          | 52 +++++++++++++++++--
  4 files changed, 68 insertions(+), 8 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 0f4dc42ec1c8..f32c7a2eec8d 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -968,11 +968,15 @@ coro_function_valid_p (tree fndecl)
if (current_function_returns_value || current_function_returns_null)
      {
-       /* TODO: record or extract positions of returns (and the first coro
-         keyword) so that we can add notes to the diagnostic about where
-         the bad keyword is and what made the function into a coro.  */
-      error_at (f_loc, "a %<return%> statement is not allowed in coroutine;"
-                       " did you mean %<co_return%>?");
+      coroutine_info *coro_info = get_or_insert_coroutine_info (fndecl);
+      auto retloc = current_function_first_return_loc;
+      gcc_checking_assert (retloc && coro_info->first_coro_keyword);
+
+      auto_diagnostic_group diaggrp;
+      error_at (retloc, "a %<return%> statement is not allowed in coroutine;"
+               " did you mean %<co_return%>?");
+      inform (coro_info->first_coro_keyword,
+             "function was made a coroutine here");
        return false;
      }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 911d1d7924cc..68c681150a1f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2123,6 +2123,8 @@ struct GTY(()) language_function {
    tree x_vtt_parm;
    tree x_return_value;
+ location_t first_return_loc;
+
    BOOL_BITFIELD returns_value : 1;
    BOOL_BITFIELD returns_null : 1;
    BOOL_BITFIELD returns_abnormally : 1;
@@ -2217,6 +2219,10 @@ struct GTY(()) language_function {
  #define current_function_return_value \
    (cp_function_chain->x_return_value)
+/* Location of the first 'return' stumbled upon during parsing. */
+
+#define current_function_first_return_loc cp_function_chain->first_return_loc
+
  /* In parser.cc.  */
  extern tree cp_literal_operator_id (const char *);
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index eb102dea8299..6cfe42f3bdd6 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -14957,6 +14957,10 @@ cp_parser_jump_statement (cp_parser* parser, tree 
&std_attrs)
              AGGR_INIT_EXPR_MUST_TAIL (ret_expr) = musttail_p;
            else
              set_musttail_on_return (expr, token->location, musttail_p);
+
+           /* Save where we saw this keyword.  */
+           if (current_function_first_return_loc == UNKNOWN_LOCATION)
+             current_function_first_return_loc = token->location;
          }
/* Build the return-statement, check co-return first, since type
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C 
b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
index 148ee4543e87..1e5d9e7a65a1 100644
--- a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
@@ -27,6 +27,7 @@ struct Coro {
    auto final_suspend () noexcept { return coro::suspend_always{}; }
    void return_void () { }
     void unhandled_exception() { }
+  auto yield_value (auto) noexcept { return coro::suspend_always{}; }
    };
  };
@@ -34,10 +35,55 @@ extern int x; // Diagnose disallowed "return" in coroutine.
  Coro
-bar () // { dg-error {a 'return' statement is not allowed} }
+bar ()
  {
    if (x)
-    return Coro();
+    return Coro();  // { dg-error {a 'return' statement is not allowed} }
    else
-    co_return;
+    co_return; // { dg-note "function was made a coroutine here" }
+}
+
+Coro
+bar1 ()
+{
+  if (x)
+    return Coro();  // { dg-error {a 'return' statement is not allowed} }
+  else
+    co_await std::suspend_never{}; // { dg-note "function was made a coroutine 
here" }
+}
+
+Coro
+bar2 ()
+{
+  if (x)
+    return Coro();  // { dg-error {a 'return' statement is not allowed} }
+  else
+    co_yield 123; // { dg-note "function was made a coroutine here" }
+}
+
+Coro
+bar3 ()
+{
+  if (x)
+    co_return; // { dg-note "function was made a coroutine here" }
+  else
+    return Coro();  // { dg-error {a 'return' statement is not allowed} }
+}
+
+Coro
+bar4 ()
+{
+  if (x)
+    co_await std::suspend_never{}; // { dg-note "function was made a coroutine 
here" }
+  else
+    return Coro();  // { dg-error {a 'return' statement is not allowed} }
+}
+
+Coro
+bar5 ()
+{
+  if (x)
+    co_yield 123; // { dg-note "function was made a coroutine here" }
+  else
+    return Coro();  // { dg-error {a 'return' statement is not allowed} }
  }

Reply via email to