1 //  { dg-do run }
2 
3 // Test exceptions.
4 
5 #include "../coro.h"
6 #include <exception>
7 
8 int gX = 0;
9 
10 struct coro1 {
11   struct promise_type;
12   using handle_type = coro::coroutine_handle<coro1::promise_type>;
13   handle_type handle;
coro1coro114   coro1 () : handle(0) {}
coro1coro115   coro1 (handle_type _handle)
16     : handle(_handle) {
17         PRINT("Created coro1 object from handle");
18   }
19   coro1 (const coro1 &) = delete; // no copying
coro1coro120   coro1 (coro1 &&s) : handle(s.handle) {
21     s.handle = nullptr;
22     PRINT("coro1 mv ctor ");
23   }
24   coro1 &operator = (coro1 &&s) {
25     handle = s.handle;
26     s.handle = nullptr;
27     PRINT("coro1 op=  ");
28     return *this;
29   }
~coro1coro130   ~coro1() {
31     PRINT("Destroyed coro1");
32     if ( handle )
33       handle.destroy();
34   }
35 
36   struct suspend_never_prt {
await_readycoro1::suspend_never_prt37     bool await_ready() const noexcept { return true; }
await_suspendcoro1::suspend_never_prt38     void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
await_resumecoro1::suspend_never_prt39     void await_resume() const noexcept { PRINT ("susp-never-resume");}
40   };
41 
42   /* NOTE: this has a DTOR to test that pathway.  */
43   struct  suspend_always_prt {
await_readycoro1::suspend_always_prt44     bool await_ready() const noexcept { return false; }
await_suspendcoro1::suspend_always_prt45     void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
await_resumecoro1::suspend_always_prt46     void await_resume() const noexcept { PRINT ("susp-always-resume"); }
~suspend_always_prtcoro1::suspend_always_prt47     ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
48   };
49 
50   struct promise_type {
51   int value;
promise_typecoro1::promise_type52   promise_type() {  PRINT ("Created Promise"); }
~promise_typecoro1::promise_type53   ~promise_type() { PRINT ("Destroyed Promise"); }
54 
get_return_objectcoro1::promise_type55   auto get_return_object () {
56     PRINT ("get_return_object: handle from promise");
57     return handle_type::from_promise (*this);
58   }
initial_suspendcoro1::promise_type59   auto initial_suspend () {
60     PRINT ("get initial_suspend (always)");
61     return suspend_always_prt{};
62   }
final_suspendcoro1::promise_type63   auto final_suspend () noexcept {
64     PRINT ("get final_suspend (always)");
65     return suspend_always_prt{};
66   }
return_valuecoro1::promise_type67   void return_value (int v) {
68     PRINTF ("return_value () %d\n",v);
69     value = v;
70   }
yield_valuecoro1::promise_type71   auto yield_value (int v) {
72     PRINTF ("yield_value () %d and suspend always\n",v);
73     value = v;
74     return suspend_always_prt{};
75   }
76   /* Some non-matching overloads.  */
yield_valuecoro1::promise_type77   auto yield_value (suspend_always_prt s, int x) {
78     return s;
79   }
yield_valuecoro1::promise_type80   auto yield_value (void) {
81     return 42;//suspend_always_prt{};
82   }
get_valuecoro1::promise_type83   int get_value (void) { return value; }
84 
unhandled_exceptioncoro1::promise_type85   void unhandled_exception() {
86     PRINT ("unhandled_exception: caught one!");
87     gX = -1;
88     // returning from here should end up in final_suspend.
89     }
90   };
91 };
92 
93 // So we want to check that the internal behaviour of try/catch is
94 // working OK - and that if we have an unhandled exception it is caught
95 // by the wrapper that we add to the rewritten func.
96 
throw_and_catch()97 struct coro1 throw_and_catch () noexcept
98 {
99   int caught = 0;
100 
101   try {
102     PRINT ("f: about to yield 42");
103     co_yield 42;
104 
105     throw (20);
106 
107     PRINT ("f: about to yield 6174");
108     co_return 6174;
109 
110   } catch (int x) {
111     PRINTF ("f: caught %d\n", x);
112     caught = x;
113   }
114 
115   PRINTF ("f: about to yield what we caught %d\n", caught);
116   co_yield caught;
117 
118   throw ("bah");
119 
120   PRINT ("f: about to return 22");
121   co_return 22;
122 }
123 
main()124 int main ()
125 {
126   PRINT ("main: create coro1");
127   struct coro1 x = throw_and_catch ();
128   if (x.handle.done())
129     abort();
130   x.handle.resume();
131   PRINT ("main: got coro, resuming..");
132   int y = x.handle.promise().get_value();
133   if ( y != 42 )
134     abort ();
135   PRINT ("main: apparently got the expected 42");
136   if (x.handle.done())
137     abort();
138   PRINT ("main: resuming...");
139   x.handle.resume();
140 
141   y = x.handle.promise().get_value();
142   if ( y != 20 )
143     abort ();
144   PRINT ("main: apparently got 20, which we expected");
145   if (x.handle.done())
146     abort();
147 
148   PRINT ("main: resuming...");
149   x.handle.resume();
150   // This should cause the throw of "bah" which is unhandled.
151   // We should catch the unhandled exception and then fall through
152   // to the final suspend point... thus be "done".
153   if (!x.handle.done())
154     {
155       PRINT ("main: apparently not done...");
156       abort ();
157     }
158   // When we caught the unhandled exception we flagged it instead of
159   // std::terminate-ing.
160   if (gX != -1)
161     {
162       PRINT ("main: apparently failed to catch");
163       abort ();
164     }
165   PRINT ("main: returning");
166   return 0;
167 }
168