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; }
