This came up in discussion and was relatively easy to 'fix' although it remains to be seen whether it was really intended. Tested on x86_64-darwin21. OK for trunk? Iain
--- >8 --- As of now the standard does not appear to forbid this, and clang accepts. The use-cases are somewhat unclear (question placed with the designers) but implemented here for sake of compatibility. Signed-off-by: Iain Sandoe <i...@sandoe.co.uk> gcc/cp/ChangeLog: * coroutines.cc (coro_build_actor_or_destroy_function): Accept coroutines that are 'extern "C"' and build unmangled names for the actor and destroy functions. gcc/testsuite/ChangeLog: * g++.dg/coroutines/torture/extern-c-coroutine.C: New test. --- gcc/cp/coroutines.cc | 12 +++ .../coroutines/torture/extern-c-coroutine.C | 89 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/extern-c-coroutine.C diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 3f23317a315..c1a0d6c2283 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see #include "tree.h" #include "gcc-rich-location.h" #include "hash-map.h" +#include "cgraph.h" static bool coro_promise_type_found_p (tree, location_t); @@ -4060,6 +4061,17 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type, else info->destroy_decl = fn; } + /* If the function is extern "C" the mangler will not be called. */ + if (DECL_EXTERN_C_P (orig)) + { + /* clone_function_name () picks up the name from DECL_ASSEMBLER_NAME + which is not yet set here. */ + SET_DECL_ASSEMBLER_NAME (fn, DECL_NAME (orig)); + if (actor_p) + SET_DECL_ASSEMBLER_NAME (fn, clone_function_name (fn, "actor")); + else + SET_DECL_ASSEMBLER_NAME (fn, clone_function_name (fn, "destroy")); + } return fn; } diff --git a/gcc/testsuite/g++.dg/coroutines/torture/extern-c-coroutine.C b/gcc/testsuite/g++.dg/coroutines/torture/extern-c-coroutine.C new file mode 100644 index 00000000000..c178a80ee4b --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/extern-c-coroutine.C @@ -0,0 +1,89 @@ +#include <coroutine> +#include <cstdio> + +#ifndef OUTPUT +# define PRINT(X) +# define PRINTF(X,...) +#else +# define PRINT(X) puts(X) +# define PRINTF printf +#endif + +struct future { + struct promise_type; + using handle_type = std::coroutine_handle<future::promise_type>; + handle_type handle; + future () : handle(0) {} + future (handle_type _handle) + : handle(_handle) { + PRINT("Created future object from handle"); + } + future (const future &) = delete; // no copying + future (future &&s) : handle(s.handle) { + s.handle = nullptr; + PRINT("future mv ctor "); + } + future &operator = (future &&s) { + handle = s.handle; + s.handle = nullptr; + PRINT("future op= "); + return *this; + } + ~future() { + PRINT("Destroyed future"); + if ( handle ) + handle.destroy(); + } + + struct promise_type { + void return_value (int v) { + PRINTF ("return_value (%d)\n", v); + vv = v; + } + + std::suspend_always initial_suspend() noexcept { return {}; } + std::suspend_always final_suspend() noexcept { return {}; } + void unhandled_exception() {} + auto get_return_object() {return handle_type::from_promise (*this);} + + int get_value () { return vv; } + private: + int vv; + }; + bool await_ready() { return false; } + void await_suspend(std::coroutine_handle<>) {} + void await_resume() {} +}; + +extern "C" future +test () { + co_return 22; +} + +extern "C" future +f () noexcept +{ + PRINT ("future: about to return"); + co_return 42; +} + +int main () +{ + PRINT ("main: create future"); + future x = f (); + PRINT ("main: got future - resuming"); + if (x.handle.done()) + __builtin_abort (); + x.handle.resume(); + PRINT ("main: after resume"); + int y = x.handle.promise().get_value(); + if ( y != 42 ) + __builtin_abort (); + if (!x.handle.done()) + { + PRINT ("main: apparently not done..."); + __builtin_abort (); + } + PRINT ("main: returning"); + return 0; +} -- 2.37.1 (Apple Git-137.1)