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

commit r17-4857-g4bf11aca586c9eef753b6f76d29ae19e1b505694
Author: Jakub Jelinek <[email protected]>
Date:   Sat Oct 3 21:30:31 2026 +0200

    c++: Implement C++29 P3950R1 - return_value & return_void Are Not Mutually 
Exclusive paper as DR [PR125830]
    
    The following patch attempts to implement this paper by dropping the
    coroutine diagnostics about both return_void and return_value.
    Also, I've added complain argument to some functions, because
    https://eel.is/c++draft/stmt.return.coroutine#3
    now says If overload resolution for p.return_void() succeeds, ...
    So, we shouldn't error in any way if p.return_void() overload resolution
    fails.
    
    2026-10-03  Jakub Jelinek  <[email protected]>
    
            P3950R1
            PR c++/125830
            * c-cppbuiltin.cc (c_cpp_builtins): Bump __cpp_impl_coroutine
            from 201902L to 202606L.
    
            * coroutines.cc: Implement C++29 P3950R1 - return_value & 
return_void
            Are Not Mutually Exclusive paper as DR.
            (struct coroutine_info): Remove coro_co_return_error_emitted member.
            (coro_promise_type_found_p): Remove diagnostics about both 
return_void
            and return_value successful lookup.
            (lookup_promise_method): Add complain argument, pass it down to
            lookup_member.
            (coro_build_promise_expression): Add complain argument, pass it down
            to lookup_promise_method, build_new_method_call and
            build_class_member_access_expr.
            (get_coroutine_return_void_expr): Pass tf_none to
            coro_build_promise_expression.
    
            * g++.dg/DRs/dr2581-2.C (__cpp_impl_coroutine): Redefine to 202606L
            rather than 201902L.
            * g++.dg/coroutines/pr105301.C: Change dg-error to dg-bogus.
            * g++.dg/coroutines/coro-pre-proc.C: Expect different value of
            __cpp_impl_coroutine.
            * g++.dg/coroutines/pr97438.C: Change dg-error to dg-bogus.
            * g++.dg/coroutines/p3950r1.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/c-family/c-cppbuiltin.cc                    |  2 +-
 gcc/cp/coroutines.cc                            | 50 ++++-------------
 gcc/testsuite/g++.dg/DRs/dr2581-2.C             |  2 +-
 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C |  2 +-
 gcc/testsuite/g++.dg/coroutines/p3950r1.C       | 74 +++++++++++++++++++++++++
 gcc/testsuite/g++.dg/coroutines/pr105301.C      |  2 +-
 gcc/testsuite/g++.dg/coroutines/pr97438.C       |  2 +-
 7 files changed, 89 insertions(+), 45 deletions(-)

diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index 5c90df81f9ce..3f27f77a2265 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1152,7 +1152,7 @@ c_cpp_builtins (cpp_reader *pfile)
       else if (cxx_dialect >= cxx20)
        cpp_warn (pfile, "__cpp_modules");
       if (flag_coroutines)
-       cpp_define (pfile, "__cpp_impl_coroutine=201902L"); /* n4861, DIS */
+       cpp_define (pfile, "__cpp_impl_coroutine=202606L"); /* n4861, DIS */
       else if (cxx_dialect >= cxx20)
        cpp_warn (pfile, "__cpp_impl_coroutine");
       if (flag_tm)
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index ce9c1b6bffab..5a04cc408967 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -298,7 +298,6 @@ struct GTY((for_user)) coroutine_info
   /* Flags to avoid repeated errors for per-function issues.  */
   bool coro_ret_type_error_emitted;
   bool coro_promise_error_emitted;
-  bool coro_co_return_error_emitted;
 };
 
 struct coroutine_info_hasher : ggc_ptr_hash<coroutine_info>
@@ -825,37 +824,6 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
          return false;
        }
 
-      /* Test for errors in the promise type that can be determined now.  */
-      tree has_ret_void = lookup_member (coro_info->promise_type,
-                                        coro_return_void_identifier,
-                                        /*protect=*/1, /*want_type=*/0,
-                                        tf_none);
-      tree has_ret_val = lookup_member (coro_info->promise_type,
-                                       coro_return_value_identifier,
-                                       /*protect=*/1, /*want_type=*/0,
-                                       tf_none);
-      if (has_ret_void && has_ret_val)
-       {
-         auto_diagnostic_group d;
-         location_t ploc = DECL_SOURCE_LOCATION (fndecl);
-         if (!coro_info->coro_co_return_error_emitted)
-           error_at (ploc, "the coroutine promise type %qT declares both"
-                     " %<return_value%> and %<return_void%>",
-                     coro_info->promise_type);
-         inform (DECL_SOURCE_LOCATION (BASELINK_FUNCTIONS (has_ret_void)),
-                 "%<return_void%> declared here");
-         has_ret_val = BASELINK_FUNCTIONS (has_ret_val);
-         const char *message = "%<return_value%> declared here";
-         if (TREE_CODE (has_ret_val) == OVERLOAD)
-           {
-             has_ret_val = OVL_FIRST (has_ret_val);
-             message = "%<return_value%> first declared here";
-           }
-         inform (DECL_SOURCE_LOCATION (has_ret_val), message);
-         coro_info->coro_co_return_error_emitted = true;
-         return false;
-       }
-
       /* Try to find the handle type for the promise.  */
       tree handle_type
        = instantiate_coro_handle_for_promise_type (loc, 
coro_info->promise_type);
@@ -1028,12 +996,13 @@ get_coroutine_from_address (tree decl)
 
 static tree
 lookup_promise_method (tree fndecl, tree member_id, location_t loc,
-                      bool musthave)
+                      bool musthave,
+                      tsubst_flags_t complain = tf_warning_or_error)
 {
   tree promise = get_coroutine_promise_type (fndecl);
   tree pm_memb
     = lookup_member (promise, member_id,
-                    /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
+                    /*protect=*/1, /*want_type=*/0, complain);
   if (musthave && pm_memb == NULL_TREE)
     {
       error_at (loc, "no member named %qE in %qT", member_id, promise);
@@ -1051,9 +1020,10 @@ lookup_promise_method (tree fndecl, tree member_id, 
location_t loc,
 static tree
 coro_build_promise_expression (tree fn, tree promise_obj, tree member_id,
                               location_t loc, vec<tree, va_gc> **args,
-                              bool musthave)
+                              bool musthave,
+                              tsubst_flags_t complain = tf_warning_or_error)
 {
-  tree meth = lookup_promise_method (fn, member_id, loc, musthave);
+  tree meth = lookup_promise_method (fn, member_id, loc, musthave, complain);
   if (meth == error_mark_node)
     return error_mark_node;
 
@@ -1067,17 +1037,17 @@ coro_build_promise_expression (tree fn, tree 
promise_obj, tree member_id,
   tree expr;
   if (BASELINK_P (meth))
     expr = build_new_method_call (promise, meth, args, NULL_TREE,
-                                 LOOKUP_NORMAL, NULL, tf_warning_or_error);
+                                 LOOKUP_NORMAL, NULL, complain);
   else
     {
       expr = build_class_member_access_expr (promise, meth, NULL_TREE,
-                                            true, tf_warning_or_error);
+                                            true, complain);
       vec<tree, va_gc> *real_args;
       if (!args)
        real_args = make_tree_vector ();
       else
        real_args = *args;
-      expr = build_op_call (expr, &real_args, tf_warning_or_error);
+      expr = build_op_call (expr, &real_args, complain);
     }
   return expr;
 }
@@ -1094,7 +1064,7 @@ get_coroutine_return_void_expr (tree decl, location_t 
loc, bool musthave)
        info->return_void
          = coro_build_promise_expression (current_function_decl, NULL,
                                           coro_return_void_identifier,
-                                          loc, NULL, musthave);
+                                          loc, NULL, musthave, tf_none);
       /* Don't return an error if it's an optional call.  */
       if (!musthave && info->return_void == error_mark_node)
        return NULL_TREE;
diff --git a/gcc/testsuite/g++.dg/DRs/dr2581-2.C 
b/gcc/testsuite/g++.dg/DRs/dr2581-2.C
index d073614aa0bc..45d1d823adea 100644
--- a/gcc/testsuite/g++.dg/DRs/dr2581-2.C
+++ b/gcc/testsuite/g++.dg/DRs/dr2581-2.C
@@ -61,7 +61,7 @@
 #define __cpp_hex_float 201603L                        // { dg-error 
"'__cpp_hex_float' redefined" "" { target c++20 } }
 #define __cpp_if_consteval 202106L             // { dg-error 
"'__cpp_if_consteval' redefined" "" { target c++23 } }
 #define __cpp_if_constexpr 201606L             // { dg-error 
"'__cpp_if_constexpr' redefined" "" { target c++20 } }
-#define __cpp_impl_coroutine 201902L           // { dg-error 
"'__cpp_impl_coroutine' redefined" "" { target c++20 } }
+#define __cpp_impl_coroutine 202606L           // { dg-error 
"'__cpp_impl_coroutine' redefined" "" { target c++20 } }
 #define __cpp_impl_destroying_delete 201806L   // { dg-error 
"'__cpp_impl_destroying_delete' redefined" "" { target c++20 } }
 #define __cpp_impl_three_way_comparison 201907L        // { dg-error 
"'__cpp_impl_three_way_comparison' redefined" "" { target c++20 } }
 #define __cpp_impl_reflection 202603L          // { dg-error 
"'__cpp_impl_reflection' redefined" "" { target c++26 } }
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C 
b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
index eacdb35d3c70..d1ea52edc289 100644
--- a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
+++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
@@ -4,6 +4,6 @@
 #error "coroutines should engaged."
 #endif
 
-#if __cpp_impl_coroutine != 201902L
+#if __cpp_impl_coroutine != 202606L
 #error "coroutine version out of sync."
 #endif
diff --git a/gcc/testsuite/g++.dg/coroutines/p3950r1.C 
b/gcc/testsuite/g++.dg/coroutines/p3950r1.C
new file mode 100644
index 000000000000..d4dcc68adf57
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/p3950r1.C
@@ -0,0 +1,74 @@
+// C++29 P3950R1 - return_value & return_void Are Not Mutually Exclusive
+
+#include <coroutine>
+#include <type_traits>
+
+struct dummy_coroutine1 {};
+template <typename T>
+struct dummy_coroutine2 {};
+
+namespace std {
+
+template<>
+class coroutine_traits<::dummy_coroutine1> {
+public:
+  struct promise_type {
+    void return_value(int x) {}
+    void return_value(double x) {}
+    void return_void() {}
+    std::suspend_never initial_suspend() noexcept { return {}; }
+    std::suspend_never final_suspend() noexcept { return {}; }
+    dummy_coroutine1 get_return_object() { return {}; }
+    void unhandled_exception() {}
+  };
+};
+
+template<typename T>
+class coroutine_traits<::dummy_coroutine2<T>> {
+public:
+  struct promise_type {
+    template<typename U = T>
+    void return_value(U x) requires std::is_same_v<U, T> {}
+    void return_void() requires std::is_same_v<void, T> {}
+    std::suspend_never initial_suspend() noexcept { return {}; }
+    std::suspend_never final_suspend() noexcept { return {}; }
+    dummy_coroutine2<T> get_return_object() { return {}; }
+    void unhandled_exception() {}
+  };
+};
+
+}
+
+int x;
+
+dummy_coroutine1
+foo ()
+{
+  if (x == 1)
+    co_return 17;
+  else if (x == 2)
+    co_return 17.0;
+  else if (x == 3)
+    co_return;
+}
+
+dummy_coroutine2<void>
+bar ()
+{
+  if (x == 4)
+    co_return;
+}
+
+dummy_coroutine2<int>
+baz ()
+{
+  if (x == 5)
+    co_return 42;
+}
+
+dummy_coroutine2<double>
+qux ()
+{
+  if (x == 6)
+    co_return 42.0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105301.C 
b/gcc/testsuite/g++.dg/coroutines/pr105301.C
index b3a3fe1365e3..01fc3ccc74e5 100644
--- a/gcc/testsuite/g++.dg/coroutines/pr105301.C
+++ b/gcc/testsuite/g++.dg/coroutines/pr105301.C
@@ -46,7 +46,7 @@ struct bad_promise_6 {
 };
 
 coro<bad_promise_6>
-bad_implicit_return() // { dg-error {.aka 'bad_promise_6'. declares both 
'return_value' and 'return_void'} }
+bad_implicit_return() // { dg-bogus {.aka 'bad_promise_6'. declares both 
'return_value' and 'return_void'} }
 {
   co_await a;
 }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr97438.C 
b/gcc/testsuite/g++.dg/coroutines/pr97438.C
index 95376648ed70..0047a66d2fc4 100644
--- a/gcc/testsuite/g++.dg/coroutines/pr97438.C
+++ b/gcc/testsuite/g++.dg/coroutines/pr97438.C
@@ -25,6 +25,6 @@ public:
 }
 
 dummy_coroutine
-foo() { // { dg-error {the coroutine promise type 
'std::__n4861::coroutine_traits<dummy_coroutine>::promise_type' declares both 
'return_value' and 'return_void'} }
+foo() { // { dg-bogus {the coroutine promise type 
'std::__n4861::coroutine_traits<dummy_coroutine>::promise_type' declares both 
'return_value' and 'return_void'} }
     co_return 17;
 }

Reply via email to