c++: Implement C++29 P3950R1 - return_value & return_void Are Not Mutually Exclusive paper as DR [PR125830]

Message ID ar5IEPf94jS6l4sU@tucnak
State New
Headers
Series c++: Implement C++29 P3950R1 - return_value & return_void Are Not Mutually Exclusive paper as DR [PR125830] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed

Commit Message

Jakub Jelinek Oct. 1, 2026, 11:46 a.m. UTC
  Hi!

The following patch attempts to implement this paper by dropping the
coroutine diagnostics about both return_void and return_value (which
apparently still has various problems, so perhaps we should fix those on
release branches:
1) the error is guarded on !coro_info->coro_co_return_error_emitted
   but the inform calls are not, so the first time we run into this,
   we emit an error and 2 informs, the next time we run there we just
   confuse the user by emitting further 2 informs without emitting
   an error.  All the diagnostic handling should be guarded on
   !coro_info->coro_co_return_error_emitted I guess and only the
   return shouldn't
2) if promise_type has e.g.
        void return_value(int x) {  }
        void return_void() {}
        void return_void(int) {}
   we ICE on it or emit bogus location, because has_ret_void in
   that case is also OVERLOAD, not a decl).  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.  I admit I'm not sure if passing tf_none exactly to the functions
I've passed it down to (lookup_member and build_new_method_call or
build_class_member_access_expr) achieves that, or if those functions
can fail with further errors after overload resolution.  If the
latter, what exactly should be called with tf_none to determine whether
overload resolution succeeds?

So far lightly tested (on coroutines/* and affected tests), ok for trunk
if it passes full bootstrap/regtest?  And what to do about the above
mentioned bugs in the code being removed?  Shall I address those first
and then send this patch to remove it afterwards (and just backport that
first patch and not the second)?

2026-10-01  Jakub Jelinek  <jakub@redhat.com>

	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.


	Jakub
  

Comments

Jason Merrill Oct. 2, 2026, 9:20 p.m. UTC | #1
On 10/1/26 1:46 PM, Jakub Jelinek wrote:
> Hi!
> 
> The following patch attempts to implement this paper by dropping the
> coroutine diagnostics about both return_void and return_value (which
> apparently still has various problems, so perhaps we should fix those on
> release branches:
> 1) the error is guarded on !coro_info->coro_co_return_error_emitted
>     but the inform calls are not, so the first time we run into this,
>     we emit an error and 2 informs, the next time we run there we just
>     confuse the user by emitting further 2 informs without emitting
>     an error.  All the diagnostic handling should be guarded on
>     !coro_info->coro_co_return_error_emitted I guess and only the
>     return shouldn't
> 2) if promise_type has e.g.
>          void return_value(int x) {  }
>          void return_void() {}
>          void return_void(int) {}
>     we ICE on it or emit bogus location, because has_ret_void in
>     that case is also OVERLOAD, not a decl).  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.  I admit I'm not sure if passing tf_none exactly to the functions
> I've passed it down to (lookup_member and build_new_method_call or
> build_class_member_access_expr) achieves that, or if those functions
> can fail with further errors after overload resolution.  If the
> latter, what exactly should be called with tf_none to determine whether
> overload resolution succeeds?

Everything in that function should change tf_warning_or_error to complain.
> So far lightly tested (on coroutines/* and affected tests), ok for trunk
> if it passes full bootstrap/regtest?  And what to do about the above
> mentioned bugs in the code being removed?  Shall I address those first
> and then send this patch to remove it afterwards (and just backport that
> first patch and not the second)?

Do you think it's important to fix those bugs on release branches?  They 
sound like minor diagnostic quality issues.
> 2026-10-01  Jakub Jelinek  <jakub@redhat.com>
> 
> 	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.
> 
> --- a/gcc/c-family/c-cppbuiltin.cc	2026-09-10 16:56:41.686638893 +0200
> +++ b/gcc/c-family/c-cppbuiltin.cc	2026-10-01 12:42:04.068512619 +0200
> @@ -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)
> --- a/gcc/cp/coroutines.cc	2026-09-25 17:52:15.184532722 +0200
> +++ b/gcc/cp/coroutines.cc	2026-10-01 13:21:12.311479268 +0200
> @@ -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,
>   	  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
>   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,11 +1037,11 @@ coro_build_promise_expression (tree fn,
>     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 ();
> @@ -1094,7 +1064,7 @@ get_coroutine_return_void_expr (tree dec
>   	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;
> --- a/gcc/testsuite/g++.dg/DRs/dr2581-2.C	2026-08-06 11:48:09.058480111 +0200
> +++ b/gcc/testsuite/g++.dg/DRs/dr2581-2.C	2026-10-01 12:44:13.876768629 +0200
> @@ -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 } }
> --- a/gcc/testsuite/g++.dg/coroutines/pr105301.C	2026-03-27 10:17:15.380310407 +0100
> +++ b/gcc/testsuite/g++.dg/coroutines/pr105301.C	2026-10-01 12:40:29.792779226 +0200
> @@ -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;
>   }
> --- a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C	2026-03-27 10:17:15.379310423 +0100
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C	2026-10-01 12:44:29.900553347 +0200
> @@ -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
> --- a/gcc/testsuite/g++.dg/coroutines/pr97438.C	2026-03-27 10:17:15.384310342 +0100
> +++ b/gcc/testsuite/g++.dg/coroutines/pr97438.C	2026-10-01 12:46:40.857793918 +0200
> @@ -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;
>   }
> --- a/gcc/testsuite/g++.dg/coroutines/p3950r1.C	2026-10-01 12:52:43.323958890 +0200
> +++ b/gcc/testsuite/g++.dg/coroutines/p3950r1.C	2026-10-01 13:06:52.331832286 +0200
> @@ -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;
> +}
> 
> 	Jakub
>
  

Patch

--- a/gcc/c-family/c-cppbuiltin.cc	2026-09-10 16:56:41.686638893 +0200
+++ b/gcc/c-family/c-cppbuiltin.cc	2026-10-01 12:42:04.068512619 +0200
@@ -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)
--- a/gcc/cp/coroutines.cc	2026-09-25 17:52:15.184532722 +0200
+++ b/gcc/cp/coroutines.cc	2026-10-01 13:21:12.311479268 +0200
@@ -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,
 	  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
 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,11 +1037,11 @@  coro_build_promise_expression (tree fn,
   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 ();
@@ -1094,7 +1064,7 @@  get_coroutine_return_void_expr (tree dec
 	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;
--- a/gcc/testsuite/g++.dg/DRs/dr2581-2.C	2026-08-06 11:48:09.058480111 +0200
+++ b/gcc/testsuite/g++.dg/DRs/dr2581-2.C	2026-10-01 12:44:13.876768629 +0200
@@ -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 } }
--- a/gcc/testsuite/g++.dg/coroutines/pr105301.C	2026-03-27 10:17:15.380310407 +0100
+++ b/gcc/testsuite/g++.dg/coroutines/pr105301.C	2026-10-01 12:40:29.792779226 +0200
@@ -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;
 }
--- a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C	2026-03-27 10:17:15.379310423 +0100
+++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C	2026-10-01 12:44:29.900553347 +0200
@@ -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
--- a/gcc/testsuite/g++.dg/coroutines/pr97438.C	2026-03-27 10:17:15.384310342 +0100
+++ b/gcc/testsuite/g++.dg/coroutines/pr97438.C	2026-10-01 12:46:40.857793918 +0200
@@ -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;
 }
--- a/gcc/testsuite/g++.dg/coroutines/p3950r1.C	2026-10-01 12:52:43.323958890 +0200
+++ b/gcc/testsuite/g++.dg/coroutines/p3950r1.C	2026-10-01 13:06:52.331832286 +0200
@@ -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;
+}