#include "generator.hpp" #include <setjmp.h> using namespace std; cppcoro::generator<int> foo() { jmp_buf env; setjmp(env); co_yield 1; } int main() { return 0; } The generator.hpp file is from https://github.com/lewissbaker/cppcoro/blob/master/include/cppcoro/generator.hpp I'm getting this error: > g++ -std=c++2a -Wall -Wextra -freport-bug foo.cpp during GIMPLE pass: coro-early-expand-ifns foo.cpp: In function ‘void foo(foo()::_Z3foov.Frame*)’: foo.cpp:6:25: internal compiler error: Segmentation fault 6 | cppcoro::generator<int> foo() | ^~~ 0x19eab38 internal_error(char const*, ...) ???:0 I believe it is the setjmp that is causing the issue. This code compiles fine on clang.
Please provide a pre-processed source file (-E option).
Created attachment 53592 [details] preprocessed cpp file
Thanks, reduced to: cat preprocessed.cpp namespace std { template <typename> using remove_reference_t = int; template <typename _Result> struct coroutine_traits : _Result {}; template <typename = void> struct coroutine_handle { operator coroutine_handle<>(); }; struct suspend_always { bool await_ready() noexcept; void await_suspend(coroutine_handle<>) noexcept; void await_resume() noexcept; }; } // namespace std struct generator_promise { void get_return_object(); std::suspend_always initial_suspend(); std::suspend_always final_suspend() noexcept; std::suspend_always yield_value(std::remove_reference_t<int>); void unhandled_exception(); }; struct generator { using promise_type = generator_promise; }; void setjmp(int); int foo_env; generator foo() { setjmp(foo_env); co_yield 1; }
Of course, we should not crash in this case. However, I am curious as to how you would expect setjmp/longjmp to work with a coroutine (I recall that the coroutine implementators discussed this with, AFAIR, the outcome that it was not really supportable). Do you have an example of a complete code where setjmp/longjmp is used together with coroutines in clang or MSVC? AFAICT Lewis' code for the generator makes no use of the setjmp/longjump. The exception mechanism that *is* defined in the standard for coroutines is the regular C++ exceptions. So - we need to fix this problem, of course, but also to revisit what the correct behaviours should be for setjmp/longjmp.
The standard says: > A call to setjmp or longjmp has undefined behavior if invoked in a suspension context of a coroutine (7.6.2.4). Given that, I don't see why using setjmp in example would be useful.
(In reply to Jonathan Wakely from comment #5) > The standard says: > > > A call to setjmp or longjmp has undefined behavior if invoked in a suspension context of a coroutine (7.6.2.4). > > Given that, I don't see why using setjmp in example would be useful. Exactly, but maybe we should diagnose (we should not crash anyway).
I was trying to call libjpeg to decode images inside a generator. A snippet of code that I found online used setjmp/longjmp for error handling, hence I ran into this issue. Given that the longjmp is called while the coroutine is executing, I'd imagine that the behavior is fairly well-defined. I've since moved on to using regular c++ exceptions for error handling, and so this issue isn't affecting me anymore, but I thought it'll still be nice to get it fixed (or at least print a helpful warning/error message) so that gcc is not segfaulting
I've a WIP fix locally for the ICE (currently undergoing reg-testing). I doubt that setjmp can be used reliably in a coroutine, but, technically, I suspect it'll work if usual setjmp rules are respected (so, no returning, suspending - which involves returning, etc). I haven't tested that much, just the ICE. (In reply to Hui Peng Hu from comment #7) > I was trying to call libjpeg to decode images inside a generator. A snippet > of code that I found online used setjmp/longjmp for error handling, hence I > ran into this issue. Given that the longjmp is called while the coroutine is > executing, I'd imagine that the behavior is fairly well-defined. the standard is fairly absolute in this regard: setjmp/longjmp are UB in suspension contexts ( https://eel.is/c++draft/csetjmp.syn#2.sentence-3 )
The trunk branch has been updated by Arsen Arsenovic <arsen@gcc.gnu.org>: https://gcc.gnu.org/g:7b7ad3f4b2455072f42e7884b93fd96ebb920bc8 commit r15-3453-g7b7ad3f4b2455072f42e7884b93fd96ebb920bc8 Author: Arsen ArsenoviÄ <arsen@aarsen.me> Date: Tue Sep 3 17:14:13 2024 +0200 coros: mark .CO_YIELD as LEAF [PR106973] We rely on .CO_YIELD calls being followed by an assignment (optionally) and then a switch/if in the same basic block. This implies that a .CO_YIELD can never end a block. However, since a call to .CO_YIELD is still a call, if the function containing it calls setjmp, GCC thinks that the .CO_YIELD can introduce abnormal control flow, and generates an edge for the call. We know this is not the case; .CO_YIELD calls get removed quite early on and have no effect, and result in no other calls, so .CO_YIELD can be considered a leaf function, preventing generating an edge when calling it. PR c++/106973 - coroutine generator and setjmp PR c++/106973 gcc/ChangeLog: * internal-fn.def (CO_YIELD): Mark as ECF_LEAF. gcc/testsuite/ChangeLog: * g++.dg/coroutines/pr106973.C: New test.
fixed on trunk, waiting for possible backport