]>
Commit | Line | Data |
---|---|---|
49789fd0 IS |
1 | /* coroutine-specific state, expansions and tests. |
2 | ||
a945c346 | 3 | Copyright (C) 2018-2024 Free Software Foundation, Inc. |
49789fd0 IS |
4 | |
5 | Contributed by Iain Sandoe <iain@sandoe.co.uk> under contract to Facebook. | |
6 | ||
7 | This file is part of GCC. | |
8 | ||
9 | GCC is free software; you can redistribute it and/or modify it under | |
10 | the terms of the GNU General Public License as published by the Free | |
11 | Software Foundation; either version 3, or (at your option) any later | |
12 | version. | |
13 | ||
14 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
15 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 | for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
20 | along with GCC; see the file COPYING3. If not see | |
21 | <http://www.gnu.org/licenses/>. */ | |
22 | ||
23 | #include "config.h" | |
24 | #include "system.h" | |
25 | #include "coretypes.h" | |
26 | #include "target.h" | |
27 | #include "cp-tree.h" | |
28 | #include "stringpool.h" | |
29 | #include "stmt.h" | |
30 | #include "stor-layout.h" | |
31 | #include "tree-iterator.h" | |
32 | #include "tree.h" | |
33 | #include "gcc-rich-location.h" | |
34 | #include "hash-map.h" | |
6303cd7e | 35 | #include "coroutines.h" |
49789fd0 | 36 | |
49789fd0 IS |
37 | static bool coro_promise_type_found_p (tree, location_t); |
38 | ||
39 | /* GCC C++ coroutines implementation. | |
40 | ||
41 | The user authors a function that becomes a coroutine (lazily) by | |
42 | making use of any of the co_await, co_yield or co_return keywords. | |
43 | ||
44 | Unlike a regular function, where the activation record is placed on the | |
45 | stack, and is destroyed on function exit, a coroutine has some state that | |
46 | persists between calls - the coroutine frame (analogous to a stack frame). | |
47 | ||
48 | We transform the user's function into three pieces: | |
49 | 1. A so-called ramp function, that establishes the coroutine frame and | |
50 | begins execution of the coroutine. | |
51 | 2. An actor function that contains the state machine corresponding to the | |
52 | user's suspend/resume structure. | |
53 | 3. A stub function that calls the actor function in 'destroy' mode. | |
54 | ||
55 | The actor function is executed: | |
56 | * from "resume point 0" by the ramp. | |
57 | * from resume point N ( > 0 ) for handle.resume() calls. | |
58 | * from the destroy stub for destroy point N for handle.destroy() calls. | |
59 | ||
60 | The functions in this file carry out the necessary analysis of, and | |
61 | transforms to, the AST to perform this. | |
62 | ||
63 | The C++ coroutine design makes use of some helper functions that are | |
64 | authored in a so-called "promise" class provided by the user. | |
65 | ||
66 | At parse time (or post substitution) the type of the coroutine promise | |
67 | will be determined. At that point, we can look up the required promise | |
68 | class methods and issue diagnostics if they are missing or incorrect. To | |
69 | avoid repeating these actions at code-gen time, we make use of temporary | |
70 | 'proxy' variables for the coroutine handle and the promise - which will | |
71 | eventually be instantiated in the coroutine frame. | |
72 | ||
73 | Each of the keywords will expand to a code sequence (although co_yield is | |
74 | just syntactic sugar for a co_await). | |
75 | ||
76 | We defer the analysis and transformation until template expansion is | |
77 | complete so that we have complete types at that time. */ | |
78 | ||
79 | ||
80 | /* The state that we collect during parsing (and template expansion) for | |
81 | a coroutine. */ | |
82 | ||
83 | struct GTY((for_user)) coroutine_info | |
84 | { | |
85 | tree function_decl; /* The original function decl. */ | |
237ab3ee IS |
86 | tree actor_decl; /* The synthesized actor function. */ |
87 | tree destroy_decl; /* The synthesized destroy function. */ | |
88 | tree promise_type; /* The cached promise type for this function. */ | |
32e678b2 | 89 | tree traits_type; /* The cached traits type for this function. */ |
237ab3ee IS |
90 | tree handle_type; /* The cached coroutine handle for this function. */ |
91 | tree self_h_proxy; /* A handle instance that is used as the proxy for the | |
92 | one that will eventually be allocated in the coroutine | |
93 | frame. */ | |
49789fd0 | 94 | tree promise_proxy; /* Likewise, a proxy promise instance. */ |
5b4476a1 | 95 | tree from_address; /* handle_type from_address function. */ |
e74c7607 | 96 | tree return_void; /* The expression for p.return_void() if it exists. */ |
49789fd0 IS |
97 | location_t first_coro_keyword; /* The location of the keyword that made this |
98 | function into a coroutine. */ | |
5cca7517 AA |
99 | |
100 | /* Temporary variable number assigned by get_awaitable_var. */ | |
101 | int awaitable_number = 0; | |
102 | ||
d60c25fa IS |
103 | /* Flags to avoid repeated errors for per-function issues. */ |
104 | bool coro_ret_type_error_emitted; | |
5bc9d2f5 | 105 | bool coro_promise_error_emitted; |
b003c4b1 | 106 | bool coro_co_return_error_emitted; |
49789fd0 IS |
107 | }; |
108 | ||
109 | struct coroutine_info_hasher : ggc_ptr_hash<coroutine_info> | |
110 | { | |
111 | typedef tree compare_type; /* We only compare the function decl. */ | |
112 | static inline hashval_t hash (coroutine_info *); | |
113 | static inline hashval_t hash (const compare_type &); | |
114 | static inline bool equal (coroutine_info *, coroutine_info *); | |
115 | static inline bool equal (coroutine_info *, const compare_type &); | |
116 | }; | |
117 | ||
118 | /* This table holds all the collected coroutine state for coroutines in | |
119 | the current translation unit. */ | |
120 | ||
121 | static GTY (()) hash_table<coroutine_info_hasher> *coroutine_info_table; | |
122 | ||
a2086f98 | 123 | /* We will initialize state lazily. */ |
49789fd0 IS |
124 | static bool coro_initialized = false; |
125 | ||
126 | /* Return a hash value for the entry pointed to by INFO. | |
127 | The compare type is a tree, but the only trees we are going use are | |
128 | function decls. We use the DECL_UID as the hash value since that is | |
129 | stable across PCH. */ | |
130 | ||
131 | hashval_t | |
132 | coroutine_info_hasher::hash (coroutine_info *info) | |
133 | { | |
134 | return DECL_UID (info->function_decl); | |
135 | } | |
136 | ||
137 | /* Return a hash value for the compare value COMP. */ | |
138 | ||
139 | hashval_t | |
140 | coroutine_info_hasher::hash (const compare_type& comp) | |
141 | { | |
142 | return DECL_UID (comp); | |
143 | } | |
144 | ||
145 | /* Return true if the entries pointed to by LHS and RHS are for the | |
146 | same coroutine. */ | |
147 | ||
148 | bool | |
149 | coroutine_info_hasher::equal (coroutine_info *lhs, coroutine_info *rhs) | |
150 | { | |
151 | return lhs->function_decl == rhs->function_decl; | |
152 | } | |
153 | ||
154 | bool | |
155 | coroutine_info_hasher::equal (coroutine_info *lhs, const compare_type& rhs) | |
156 | { | |
157 | return lhs->function_decl == rhs; | |
158 | } | |
159 | ||
160 | /* Get the existing coroutine_info for FN_DECL, or insert a new one if the | |
161 | entry does not yet exist. */ | |
162 | ||
163 | coroutine_info * | |
164 | get_or_insert_coroutine_info (tree fn_decl) | |
165 | { | |
166 | gcc_checking_assert (coroutine_info_table != NULL); | |
167 | ||
168 | coroutine_info **slot = coroutine_info_table->find_slot_with_hash | |
169 | (fn_decl, coroutine_info_hasher::hash (fn_decl), INSERT); | |
170 | ||
171 | if (*slot == NULL) | |
172 | { | |
173 | *slot = new (ggc_cleared_alloc<coroutine_info> ()) coroutine_info (); | |
174 | (*slot)->function_decl = fn_decl; | |
175 | } | |
176 | ||
177 | return *slot; | |
178 | } | |
179 | ||
180 | /* Get the existing coroutine_info for FN_DECL, fail if it doesn't exist. */ | |
181 | ||
182 | coroutine_info * | |
183 | get_coroutine_info (tree fn_decl) | |
184 | { | |
d60c25fa IS |
185 | if (coroutine_info_table == NULL) |
186 | return NULL; | |
49789fd0 IS |
187 | |
188 | coroutine_info **slot = coroutine_info_table->find_slot_with_hash | |
189 | (fn_decl, coroutine_info_hasher::hash (fn_decl), NO_INSERT); | |
190 | if (slot) | |
191 | return *slot; | |
192 | return NULL; | |
193 | } | |
194 | ||
195 | /* We will lazily create all the identifiers that are used by coroutines | |
196 | on the first attempt to lookup the traits. */ | |
197 | ||
198 | /* Identifiers that are used by all coroutines. */ | |
199 | ||
200 | static GTY(()) tree coro_traits_identifier; | |
201 | static GTY(()) tree coro_handle_identifier; | |
202 | static GTY(()) tree coro_promise_type_identifier; | |
203 | ||
204 | /* Required promise method name identifiers. */ | |
205 | ||
206 | static GTY(()) tree coro_await_transform_identifier; | |
207 | static GTY(()) tree coro_initial_suspend_identifier; | |
208 | static GTY(()) tree coro_final_suspend_identifier; | |
209 | static GTY(()) tree coro_return_void_identifier; | |
210 | static GTY(()) tree coro_return_value_identifier; | |
211 | static GTY(()) tree coro_yield_value_identifier; | |
6d85947d | 212 | static GTY(()) tree coro_address_identifier; |
49789fd0 IS |
213 | static GTY(()) tree coro_from_address_identifier; |
214 | static GTY(()) tree coro_get_return_object_identifier; | |
215 | static GTY(()) tree coro_gro_on_allocation_fail_identifier; | |
216 | static GTY(()) tree coro_unhandled_exception_identifier; | |
217 | ||
218 | /* Awaitable methods. */ | |
219 | ||
220 | static GTY(()) tree coro_await_ready_identifier; | |
221 | static GTY(()) tree coro_await_suspend_identifier; | |
222 | static GTY(()) tree coro_await_resume_identifier; | |
223 | ||
c5a735fa IS |
224 | /* Accessors for the coroutine frame state used by the implementation. */ |
225 | ||
226 | static GTY(()) tree coro_resume_fn_id; | |
227 | static GTY(()) tree coro_destroy_fn_id; | |
228 | static GTY(()) tree coro_promise_id; | |
229 | static GTY(()) tree coro_frame_needs_free_id; | |
230 | static GTY(()) tree coro_resume_index_id; | |
231 | static GTY(()) tree coro_self_handle_id; | |
232 | static GTY(()) tree coro_actor_continue_id; | |
233 | static GTY(()) tree coro_frame_i_a_r_c_id; | |
234 | ||
235 | /* Create the identifiers used by the coroutines library interfaces and | |
236 | the implementation frame state. */ | |
49789fd0 IS |
237 | |
238 | static void | |
239 | coro_init_identifiers () | |
240 | { | |
241 | coro_traits_identifier = get_identifier ("coroutine_traits"); | |
242 | coro_handle_identifier = get_identifier ("coroutine_handle"); | |
243 | coro_promise_type_identifier = get_identifier ("promise_type"); | |
244 | ||
245 | coro_await_transform_identifier = get_identifier ("await_transform"); | |
246 | coro_initial_suspend_identifier = get_identifier ("initial_suspend"); | |
247 | coro_final_suspend_identifier = get_identifier ("final_suspend"); | |
248 | coro_return_void_identifier = get_identifier ("return_void"); | |
249 | coro_return_value_identifier = get_identifier ("return_value"); | |
250 | coro_yield_value_identifier = get_identifier ("yield_value"); | |
6d85947d | 251 | coro_address_identifier = get_identifier ("address"); |
49789fd0 IS |
252 | coro_from_address_identifier = get_identifier ("from_address"); |
253 | coro_get_return_object_identifier = get_identifier ("get_return_object"); | |
254 | coro_gro_on_allocation_fail_identifier = | |
255 | get_identifier ("get_return_object_on_allocation_failure"); | |
256 | coro_unhandled_exception_identifier = get_identifier ("unhandled_exception"); | |
257 | ||
258 | coro_await_ready_identifier = get_identifier ("await_ready"); | |
259 | coro_await_suspend_identifier = get_identifier ("await_suspend"); | |
260 | coro_await_resume_identifier = get_identifier ("await_resume"); | |
c5a735fa IS |
261 | |
262 | /* Coroutine state frame field accessors. */ | |
263 | coro_resume_fn_id = get_identifier ("_Coro_resume_fn"); | |
264 | coro_destroy_fn_id = get_identifier ("_Coro_destroy_fn"); | |
265 | coro_promise_id = get_identifier ("_Coro_promise"); | |
266 | coro_frame_needs_free_id = get_identifier ("_Coro_frame_needs_free"); | |
267 | coro_frame_i_a_r_c_id = get_identifier ("_Coro_initial_await_resume_called"); | |
268 | coro_resume_index_id = get_identifier ("_Coro_resume_index"); | |
269 | coro_self_handle_id = get_identifier ("_Coro_self_handle"); | |
270 | coro_actor_continue_id = get_identifier ("_Coro_actor_continue"); | |
49789fd0 IS |
271 | } |
272 | ||
273 | /* Trees we only need to set up once. */ | |
274 | ||
275 | static GTY(()) tree coro_traits_templ; | |
276 | static GTY(()) tree coro_handle_templ; | |
277 | static GTY(()) tree void_coro_handle_type; | |
5b4476a1 | 278 | static GTY(()) tree void_coro_handle_address; |
49789fd0 IS |
279 | |
280 | /* ================= Parse, Semantics and Type checking ================= */ | |
281 | ||
282 | /* This initial set of routines are helper for the parsing and template | |
283 | expansion phases. | |
284 | ||
285 | At the completion of this, we will have completed trees for each of the | |
286 | keywords, but making use of proxy variables for the self-handle and the | |
287 | promise class instance. */ | |
288 | ||
289 | /* [coroutine.traits] | |
290 | Lookup the coroutine_traits template decl. */ | |
291 | ||
292 | static tree | |
293 | find_coro_traits_template_decl (location_t kw) | |
294 | { | |
a2086f98 | 295 | /* If we are missing fundamental information, such as the traits, (or the |
d60c25fa IS |
296 | declaration found is not a type template), then don't emit an error for |
297 | every keyword in a TU, just do it once. */ | |
298 | static bool traits_error_emitted = false; | |
299 | ||
49789fd0 | 300 | tree traits_decl = lookup_qualified_name (std_node, coro_traits_identifier, |
4c58a32f | 301 | LOOK_want::NORMAL, |
d60c25fa IS |
302 | /*complain=*/!traits_error_emitted); |
303 | if (traits_decl == error_mark_node | |
304 | || !DECL_TYPE_TEMPLATE_P (traits_decl)) | |
49789fd0 | 305 | { |
d60c25fa IS |
306 | if (!traits_error_emitted) |
307 | { | |
cb76fcf5 | 308 | auto_diagnostic_group d; |
d60c25fa IS |
309 | gcc_rich_location richloc (kw); |
310 | error_at (&richloc, "coroutines require a traits template; cannot" | |
311 | " find %<%E::%E%>", std_node, coro_traits_identifier); | |
312 | inform (&richloc, "perhaps %<#include <coroutine>%> is missing"); | |
313 | traits_error_emitted = true; | |
314 | } | |
49789fd0 IS |
315 | return NULL_TREE; |
316 | } | |
317 | else | |
318 | return traits_decl; | |
319 | } | |
320 | ||
321 | /* Instantiate Coroutine traits for the function signature. */ | |
322 | ||
323 | static tree | |
324 | instantiate_coro_traits (tree fndecl, location_t kw) | |
325 | { | |
326 | /* [coroutine.traits.primary] | |
327 | So now build up a type list for the template <typename _R, typename...>. | |
328 | The types are the function's arg types and _R is the function return | |
329 | type. */ | |
330 | ||
331 | tree functyp = TREE_TYPE (fndecl); | |
f5edc76a | 332 | tree arg = DECL_ARGUMENTS (fndecl); |
49789fd0 IS |
333 | tree arg_node = TYPE_ARG_TYPES (functyp); |
334 | tree argtypes = make_tree_vec (list_length (arg_node)-1); | |
335 | unsigned p = 0; | |
336 | ||
337 | while (arg_node != NULL_TREE && !VOID_TYPE_P (TREE_VALUE (arg_node))) | |
338 | { | |
1c140cfb IS |
339 | if (is_this_parameter (arg) |
340 | || DECL_NAME (arg) == closure_identifier) | |
f5edc76a IS |
341 | { |
342 | /* We pass a reference to *this to the param preview. */ | |
343 | tree ct = TREE_TYPE (TREE_TYPE (arg)); | |
344 | TREE_VEC_ELT (argtypes, p++) = cp_build_reference_type (ct, false); | |
345 | } | |
346 | else | |
347 | TREE_VEC_ELT (argtypes, p++) = TREE_VALUE (arg_node); | |
348 | ||
49789fd0 | 349 | arg_node = TREE_CHAIN (arg_node); |
f5edc76a | 350 | arg = DECL_CHAIN (arg); |
49789fd0 IS |
351 | } |
352 | ||
353 | tree argtypepack = cxx_make_type (TYPE_ARGUMENT_PACK); | |
ee18dc41 | 354 | ARGUMENT_PACK_ARGS (argtypepack) = argtypes; |
49789fd0 IS |
355 | |
356 | tree targ = make_tree_vec (2); | |
357 | TREE_VEC_ELT (targ, 0) = TREE_TYPE (functyp); | |
358 | TREE_VEC_ELT (targ, 1) = argtypepack; | |
359 | ||
360 | tree traits_class | |
361 | = lookup_template_class (coro_traits_templ, targ, | |
80222642 | 362 | /*in_decl=*/NULL_TREE, /*context=*/NULL_TREE, |
f04dc89a | 363 | tf_warning_or_error); |
49789fd0 | 364 | |
d60c25fa | 365 | if (traits_class == error_mark_node) |
49789fd0 IS |
366 | { |
367 | error_at (kw, "cannot instantiate %<coroutine traits%>"); | |
368 | return NULL_TREE; | |
369 | } | |
370 | ||
371 | return traits_class; | |
372 | } | |
373 | ||
374 | /* [coroutine.handle] */ | |
375 | ||
376 | static tree | |
377 | find_coro_handle_template_decl (location_t kw) | |
378 | { | |
d60c25fa IS |
379 | /* As for the coroutine traits, this error is per TU, so only emit |
380 | it once. */ | |
381 | static bool coro_handle_error_emitted = false; | |
49789fd0 | 382 | tree handle_decl = lookup_qualified_name (std_node, coro_handle_identifier, |
4c58a32f NS |
383 | LOOK_want::NORMAL, |
384 | !coro_handle_error_emitted); | |
d60c25fa IS |
385 | if (handle_decl == error_mark_node |
386 | || !DECL_CLASS_TEMPLATE_P (handle_decl)) | |
49789fd0 | 387 | { |
d60c25fa IS |
388 | if (!coro_handle_error_emitted) |
389 | error_at (kw, "coroutines require a handle class template;" | |
390 | " cannot find %<%E::%E%>", std_node, coro_handle_identifier); | |
391 | coro_handle_error_emitted = true; | |
49789fd0 IS |
392 | return NULL_TREE; |
393 | } | |
394 | else | |
395 | return handle_decl; | |
396 | } | |
397 | ||
5b4476a1 AA |
398 | /* Get and validate HANDLE_TYPE::address. The resulting function, if any, will |
399 | be a non-overloaded member function that takes no arguments and returns | |
400 | void*. If that is not the case, signals an error and returns NULL_TREE. */ | |
401 | ||
402 | static tree | |
403 | get_handle_type_address (location_t kw, tree handle_type) | |
404 | { | |
405 | tree addr_getter = lookup_member (handle_type, coro_address_identifier, 1, | |
406 | 0, tf_warning_or_error); | |
407 | if (!addr_getter || addr_getter == error_mark_node) | |
408 | { | |
409 | qualified_name_lookup_error (handle_type, coro_address_identifier, | |
410 | error_mark_node, kw); | |
411 | return NULL_TREE; | |
412 | } | |
413 | ||
414 | if (!BASELINK_P (addr_getter) | |
415 | || TREE_CODE (TREE_TYPE (addr_getter)) != METHOD_TYPE) | |
416 | { | |
417 | error_at (kw, "%qE must be a non-overloaded method", addr_getter); | |
418 | return NULL_TREE; | |
419 | } | |
420 | ||
421 | tree fn_t = TREE_TYPE (addr_getter); | |
422 | tree arg = TYPE_ARG_TYPES (fn_t); | |
423 | ||
424 | /* Skip the 'this' pointer. */ | |
425 | arg = TREE_CHAIN (arg); | |
426 | ||
427 | /* Check that from_addr has the argument list (). */ | |
428 | if (arg != void_list_node) | |
429 | { | |
430 | error_at (kw, "%qE must take no arguments", addr_getter); | |
431 | return NULL_TREE; | |
432 | } | |
433 | ||
434 | tree ret_t = TREE_TYPE (fn_t); | |
435 | if (!same_type_p (ret_t, ptr_type_node)) | |
436 | { | |
437 | error_at (kw, "%qE must return %qT, not %qT", | |
438 | addr_getter, ptr_type_node, ret_t); | |
439 | return NULL_TREE; | |
440 | } | |
441 | ||
442 | return addr_getter; | |
443 | } | |
444 | ||
445 | /* Get and validate HANDLE_TYPE::from_address. The resulting function, if | |
446 | any, will be a non-overloaded static function that takes a single void* and | |
447 | returns HANDLE_TYPE. If that is not the case, signals an error and returns | |
448 | NULL_TREE. */ | |
449 | ||
450 | static tree | |
451 | get_handle_type_from_address (location_t kw, tree handle_type) | |
452 | { | |
453 | tree from_addr = lookup_member (handle_type, coro_from_address_identifier, 1, | |
454 | 0, tf_warning_or_error); | |
455 | if (!from_addr || from_addr == error_mark_node) | |
456 | { | |
457 | qualified_name_lookup_error (handle_type, coro_from_address_identifier, | |
458 | error_mark_node, kw); | |
459 | return NULL_TREE; | |
460 | } | |
461 | if (!BASELINK_P (from_addr) | |
462 | || TREE_CODE (TREE_TYPE (from_addr)) != FUNCTION_TYPE) | |
463 | { | |
464 | error_at (kw, "%qE must be a non-overloaded static function", from_addr); | |
465 | return NULL_TREE; | |
466 | } | |
467 | ||
468 | tree fn_t = TREE_TYPE (from_addr); | |
469 | tree arg = TYPE_ARG_TYPES (fn_t); | |
470 | /* Check that from_addr has the argument list (void*). */ | |
471 | if (!arg | |
472 | || !same_type_p (TREE_VALUE (arg), ptr_type_node) | |
473 | || TREE_CHAIN (arg) != void_list_node) | |
474 | { | |
475 | error_at (kw, "%qE must take a single %qT", from_addr, ptr_type_node); | |
476 | return NULL_TREE; | |
477 | } | |
478 | ||
479 | tree ret_t = TREE_TYPE (fn_t); | |
480 | if (!same_type_p (ret_t, handle_type)) | |
481 | { | |
482 | error_at (kw, "%qE must return %qT, not %qT", | |
483 | from_addr, handle_type, ret_t); | |
484 | return NULL_TREE; | |
485 | } | |
486 | ||
487 | return from_addr; | |
488 | } | |
49789fd0 IS |
489 | |
490 | static tree | |
491 | instantiate_coro_handle_for_promise_type (location_t kw, tree promise_type) | |
492 | { | |
493 | /* So now build up a type list for the template, one entry, the promise. */ | |
494 | tree targ = make_tree_vec (1); | |
495 | TREE_VEC_ELT (targ, 0) = promise_type; | |
496 | tree handle_type | |
497 | = lookup_template_class (coro_handle_identifier, targ, | |
80222642 IS |
498 | /* in_decl=*/NULL_TREE, |
499 | /* context=*/std_node, | |
f04dc89a | 500 | tf_warning_or_error); |
49789fd0 IS |
501 | |
502 | if (handle_type == error_mark_node) | |
503 | { | |
504 | error_at (kw, "cannot instantiate a %<coroutine handle%> for" | |
505 | " promise type %qT", promise_type); | |
506 | return NULL_TREE; | |
507 | } | |
508 | ||
509 | return handle_type; | |
510 | } | |
511 | ||
512 | /* Look for the promise_type in the instantiated traits. */ | |
513 | ||
514 | static tree | |
515 | find_promise_type (tree traits_class) | |
516 | { | |
517 | tree promise_type | |
518 | = lookup_member (traits_class, coro_promise_type_identifier, | |
80222642 | 519 | /* protect=*/1, /*want_type=*/true, tf_warning_or_error); |
49789fd0 IS |
520 | |
521 | if (promise_type) | |
522 | promise_type | |
523 | = complete_type_or_else (TREE_TYPE (promise_type), promise_type); | |
524 | ||
525 | /* NULL_TREE on fail. */ | |
526 | return promise_type; | |
527 | } | |
528 | ||
32e678b2 AA |
529 | /* Perform initialization of the coroutine processor state, if not done |
530 | before. */ | |
531 | ||
49789fd0 | 532 | static bool |
32e678b2 | 533 | ensure_coro_initialized (location_t loc) |
49789fd0 | 534 | { |
49789fd0 IS |
535 | if (!coro_initialized) |
536 | { | |
d60c25fa IS |
537 | /* Trees we only need to create once. |
538 | Set up the identifiers we will use. */ | |
49789fd0 | 539 | coro_init_identifiers (); |
d60c25fa | 540 | |
49789fd0 IS |
541 | /* Coroutine traits template. */ |
542 | coro_traits_templ = find_coro_traits_template_decl (loc); | |
d60c25fa IS |
543 | if (coro_traits_templ == NULL_TREE) |
544 | return false; | |
545 | ||
49789fd0 IS |
546 | /* coroutine_handle<> template. */ |
547 | coro_handle_templ = find_coro_handle_template_decl (loc); | |
d60c25fa IS |
548 | if (coro_handle_templ == NULL_TREE) |
549 | return false; | |
550 | ||
49789fd0 | 551 | /* We can also instantiate the void coroutine_handle<> */ |
5b4476a1 AA |
552 | void_coro_handle_type |
553 | = instantiate_coro_handle_for_promise_type (loc, void_type_node); | |
d60c25fa IS |
554 | if (void_coro_handle_type == NULL_TREE) |
555 | return false; | |
556 | ||
5b4476a1 AA |
557 | void_coro_handle_address |
558 | = get_handle_type_address (loc, void_coro_handle_type); | |
559 | if (!void_coro_handle_address) | |
560 | return false; | |
561 | ||
d60c25fa IS |
562 | /* A table to hold the state, per coroutine decl. */ |
563 | gcc_checking_assert (coroutine_info_table == NULL); | |
564 | coroutine_info_table = | |
565 | hash_table<coroutine_info_hasher>::create_ggc (11); | |
566 | ||
567 | if (coroutine_info_table == NULL) | |
568 | return false; | |
569 | ||
49789fd0 IS |
570 | coro_initialized = true; |
571 | } | |
32e678b2 AA |
572 | return true; |
573 | } | |
574 | ||
575 | /* Try to get the coroutine traits class. */ | |
576 | static tree | |
577 | coro_get_traits_class (tree fndecl, location_t loc) | |
578 | { | |
579 | gcc_assert (fndecl != NULL_TREE); | |
580 | gcc_assert (coro_initialized); | |
581 | ||
582 | coroutine_info *coro_info = get_or_insert_coroutine_info (fndecl); | |
583 | auto& traits_type = coro_info->traits_type; | |
584 | if (!traits_type) | |
585 | traits_type = instantiate_coro_traits (fndecl, loc); | |
586 | return traits_type; | |
587 | } | |
588 | ||
589 | static bool | |
590 | coro_promise_type_found_p (tree fndecl, location_t loc) | |
591 | { | |
592 | gcc_assert (fndecl != NULL_TREE); | |
593 | ||
594 | if (!ensure_coro_initialized (loc)) | |
595 | return false; | |
49789fd0 | 596 | |
d60c25fa IS |
597 | /* Save the coroutine data on the side to avoid the overhead on every |
598 | function decl tree. */ | |
599 | ||
49789fd0 IS |
600 | coroutine_info *coro_info = get_or_insert_coroutine_info (fndecl); |
601 | /* Without this, we cannot really proceed. */ | |
602 | gcc_checking_assert (coro_info); | |
603 | ||
604 | /* If we don't already have a current promise type, try to look it up. */ | |
605 | if (coro_info->promise_type == NULL_TREE) | |
606 | { | |
607 | /* Get the coroutine traits template class instance for the function | |
608 | signature we have - coroutine_traits <R, ...> */ | |
d60c25fa | 609 | |
32e678b2 | 610 | tree templ_class = coro_get_traits_class (fndecl, loc); |
49789fd0 IS |
611 | |
612 | /* Find the promise type for that. */ | |
613 | coro_info->promise_type = find_promise_type (templ_class); | |
614 | ||
615 | /* If we don't find it, punt on the rest. */ | |
616 | if (coro_info->promise_type == NULL_TREE) | |
617 | { | |
5bc9d2f5 IS |
618 | if (!coro_info->coro_promise_error_emitted) |
619 | error_at (loc, "unable to find the promise type for" | |
620 | " this coroutine"); | |
621 | coro_info->coro_promise_error_emitted = true; | |
49789fd0 IS |
622 | return false; |
623 | } | |
624 | ||
b003c4b1 IS |
625 | /* Test for errors in the promise type that can be determined now. */ |
626 | tree has_ret_void = lookup_member (coro_info->promise_type, | |
627 | coro_return_void_identifier, | |
628 | /*protect=*/1, /*want_type=*/0, | |
629 | tf_none); | |
630 | tree has_ret_val = lookup_member (coro_info->promise_type, | |
631 | coro_return_value_identifier, | |
632 | /*protect=*/1, /*want_type=*/0, | |
633 | tf_none); | |
634 | if (has_ret_void && has_ret_val) | |
635 | { | |
cb76fcf5 | 636 | auto_diagnostic_group d; |
b003c4b1 IS |
637 | location_t ploc = DECL_SOURCE_LOCATION (fndecl); |
638 | if (!coro_info->coro_co_return_error_emitted) | |
639 | error_at (ploc, "the coroutine promise type %qT declares both" | |
640 | " %<return_value%> and %<return_void%>", | |
641 | coro_info->promise_type); | |
642 | inform (DECL_SOURCE_LOCATION (BASELINK_FUNCTIONS (has_ret_void)), | |
643 | "%<return_void%> declared here"); | |
6cae3bb6 IS |
644 | has_ret_val = BASELINK_FUNCTIONS (has_ret_val); |
645 | const char *message = "%<return_value%> declared here"; | |
646 | if (TREE_CODE (has_ret_val) == OVERLOAD) | |
647 | { | |
648 | has_ret_val = OVL_FIRST (has_ret_val); | |
649 | message = "%<return_value%> first declared here"; | |
650 | } | |
651 | inform (DECL_SOURCE_LOCATION (has_ret_val), message); | |
b003c4b1 IS |
652 | coro_info->coro_co_return_error_emitted = true; |
653 | return false; | |
654 | } | |
655 | ||
49789fd0 | 656 | /* Try to find the handle type for the promise. */ |
5b4476a1 AA |
657 | tree handle_type |
658 | = instantiate_coro_handle_for_promise_type (loc, coro_info->promise_type); | |
49789fd0 IS |
659 | if (handle_type == NULL_TREE) |
660 | return false; | |
5b4476a1 AA |
661 | tree from_address = get_handle_type_from_address (loc, handle_type); |
662 | if (from_address == NULL_TREE) | |
663 | return false; | |
49789fd0 IS |
664 | |
665 | /* Complete this, we're going to use it. */ | |
666 | coro_info->handle_type = complete_type_or_else (handle_type, fndecl); | |
5b4476a1 | 667 | coro_info->from_address = from_address; |
a312c801 | 668 | |
49789fd0 | 669 | /* Diagnostic would be emitted by complete_type_or_else. */ |
a312c801 | 670 | if (!coro_info->handle_type) |
49789fd0 IS |
671 | return false; |
672 | ||
673 | /* Build a proxy for a handle to "self" as the param to | |
674 | await_suspend() calls. */ | |
675 | coro_info->self_h_proxy | |
c5a735fa | 676 | = build_lang_decl (VAR_DECL, coro_self_handle_id, |
49789fd0 IS |
677 | coro_info->handle_type); |
678 | ||
679 | /* Build a proxy for the promise so that we can perform lookups. */ | |
680 | coro_info->promise_proxy | |
c5a735fa | 681 | = build_lang_decl (VAR_DECL, coro_promise_id, |
49789fd0 IS |
682 | coro_info->promise_type); |
683 | ||
684 | /* Note where we first saw a coroutine keyword. */ | |
685 | coro_info->first_coro_keyword = loc; | |
686 | } | |
687 | ||
688 | return true; | |
689 | } | |
690 | ||
237ab3ee IS |
691 | /* Map from actor or destroyer to ramp. */ |
692 | static GTY(()) hash_map<tree, tree> *to_ramp; | |
693 | ||
694 | /* Given a tree that is an actor or destroy, find the ramp function. */ | |
695 | ||
696 | tree | |
697 | coro_get_ramp_function (tree decl) | |
698 | { | |
699 | if (!to_ramp) | |
700 | return NULL_TREE; | |
701 | tree *p = to_ramp->get (decl); | |
702 | if (p) | |
703 | return *p; | |
704 | return NULL_TREE; | |
705 | } | |
706 | ||
707 | /* Given the DECL for a ramp function (the user's original declaration) return | |
708 | the actor function if it has been defined. */ | |
709 | ||
710 | tree | |
711 | coro_get_actor_function (tree decl) | |
712 | { | |
713 | if (coroutine_info *info = get_coroutine_info (decl)) | |
714 | return info->actor_decl; | |
715 | ||
716 | return NULL_TREE; | |
717 | } | |
718 | ||
719 | /* Given the DECL for a ramp function (the user's original declaration) return | |
720 | the destroy function if it has been defined. */ | |
721 | ||
722 | tree | |
723 | coro_get_destroy_function (tree decl) | |
724 | { | |
725 | if (coroutine_info *info = get_coroutine_info (decl)) | |
726 | return info->destroy_decl; | |
727 | ||
728 | return NULL_TREE; | |
729 | } | |
730 | ||
2664c1bf AA |
731 | /* Given a CO_AWAIT_EXPR AWAIT_EXPR, return its resume call. */ |
732 | ||
733 | tree* | |
734 | co_await_get_resume_call (tree await_expr) | |
735 | { | |
736 | gcc_checking_assert (TREE_CODE (await_expr) == CO_AWAIT_EXPR); | |
737 | tree vec = TREE_OPERAND (await_expr, 3); | |
738 | if (!vec) | |
739 | return nullptr; | |
740 | return &TREE_VEC_ELT (vec, 2); | |
741 | } | |
742 | ||
743 | ||
49789fd0 IS |
744 | /* These functions assumes that the caller has verified that the state for |
745 | the decl has been initialized, we try to minimize work here. */ | |
746 | ||
747 | static tree | |
748 | get_coroutine_promise_type (tree decl) | |
749 | { | |
750 | if (coroutine_info *info = get_coroutine_info (decl)) | |
751 | return info->promise_type; | |
752 | ||
753 | return NULL_TREE; | |
754 | } | |
755 | ||
756 | static tree | |
757 | get_coroutine_handle_type (tree decl) | |
758 | { | |
759 | if (coroutine_info *info = get_coroutine_info (decl)) | |
760 | return info->handle_type; | |
761 | ||
762 | return NULL_TREE; | |
763 | } | |
764 | ||
765 | static tree | |
766 | get_coroutine_self_handle_proxy (tree decl) | |
767 | { | |
768 | if (coroutine_info *info = get_coroutine_info (decl)) | |
769 | return info->self_h_proxy; | |
770 | ||
771 | return NULL_TREE; | |
772 | } | |
773 | ||
774 | static tree | |
775 | get_coroutine_promise_proxy (tree decl) | |
776 | { | |
777 | if (coroutine_info *info = get_coroutine_info (decl)) | |
778 | return info->promise_proxy; | |
779 | ||
780 | return NULL_TREE; | |
781 | } | |
782 | ||
5b4476a1 AA |
783 | static tree |
784 | get_coroutine_from_address (tree decl) | |
785 | { | |
786 | if (coroutine_info *info = get_coroutine_info (decl)) | |
787 | return info->from_address; | |
788 | ||
789 | return NULL_TREE; | |
790 | } | |
791 | ||
49789fd0 IS |
792 | static tree |
793 | lookup_promise_method (tree fndecl, tree member_id, location_t loc, | |
794 | bool musthave) | |
795 | { | |
796 | tree promise = get_coroutine_promise_type (fndecl); | |
797 | tree pm_memb | |
798 | = lookup_member (promise, member_id, | |
c75199b7 J |
799 | /*protect=*/1, /*want_type=*/0, tf_warning_or_error); |
800 | if (musthave && pm_memb == NULL_TREE) | |
49789fd0 IS |
801 | { |
802 | error_at (loc, "no member named %qE in %qT", member_id, promise); | |
803 | return error_mark_node; | |
804 | } | |
805 | return pm_memb; | |
806 | } | |
807 | ||
e74c7607 IS |
808 | /* Build an expression of the form p.method (args) where the p is a promise |
809 | object for the current coroutine. | |
810 | OBJECT is the promise object instance to use, it may be NULL, in which case | |
811 | we will use the promise_proxy instance for this coroutine. | |
812 | ARGS may be NULL, for empty parm lists. */ | |
813 | ||
814 | static tree | |
815 | coro_build_promise_expression (tree fn, tree promise_obj, tree member_id, | |
816 | location_t loc, vec<tree, va_gc> **args, | |
817 | bool musthave) | |
818 | { | |
819 | tree meth = lookup_promise_method (fn, member_id, loc, musthave); | |
820 | if (meth == error_mark_node) | |
821 | return error_mark_node; | |
822 | ||
823 | /* If we don't find it, and it isn't needed, an empty return is OK. */ | |
824 | if (!meth) | |
825 | return NULL_TREE; | |
826 | ||
827 | tree promise | |
828 | = promise_obj ? promise_obj | |
829 | : get_coroutine_promise_proxy (current_function_decl); | |
830 | tree expr; | |
831 | if (BASELINK_P (meth)) | |
832 | expr = build_new_method_call (promise, meth, args, NULL_TREE, | |
833 | LOOKUP_NORMAL, NULL, tf_warning_or_error); | |
834 | else | |
835 | { | |
836 | expr = build_class_member_access_expr (promise, meth, NULL_TREE, | |
837 | true, tf_warning_or_error); | |
838 | vec<tree, va_gc> *real_args; | |
839 | if (!args) | |
840 | real_args = make_tree_vector (); | |
841 | else | |
842 | real_args = *args; | |
843 | expr = build_op_call (expr, &real_args, tf_warning_or_error); | |
844 | } | |
845 | return expr; | |
846 | } | |
847 | ||
848 | /* Caching get for the expression p.return_void (). */ | |
849 | ||
850 | static tree | |
851 | get_coroutine_return_void_expr (tree decl, location_t loc, bool musthave) | |
852 | { | |
853 | if (coroutine_info *info = get_coroutine_info (decl)) | |
854 | { | |
855 | /* If we don't have it try to build it. */ | |
856 | if (!info->return_void) | |
857 | info->return_void | |
858 | = coro_build_promise_expression (current_function_decl, NULL, | |
859 | coro_return_void_identifier, | |
860 | loc, NULL, musthave); | |
861 | /* Don't return an error if it's an optional call. */ | |
862 | if (!musthave && info->return_void == error_mark_node) | |
863 | return NULL_TREE; | |
864 | return info->return_void; | |
865 | } | |
866 | return musthave ? error_mark_node : NULL_TREE; | |
867 | } | |
868 | ||
c75199b7 J |
869 | /* Lookup an Awaitable member, which should be await_ready, await_suspend |
870 | or await_resume. */ | |
871 | ||
872 | static tree | |
873 | lookup_awaitable_member (tree await_type, tree member_id, location_t loc) | |
874 | { | |
875 | tree aw_memb | |
876 | = lookup_member (await_type, member_id, | |
877 | /*protect=*/1, /*want_type=*/0, tf_warning_or_error); | |
878 | if (aw_memb == NULL_TREE) | |
879 | { | |
880 | error_at (loc, "no member named %qE in %qT", member_id, await_type); | |
881 | return error_mark_node; | |
882 | } | |
883 | return aw_memb; | |
884 | } | |
885 | ||
49789fd0 IS |
886 | /* Here we check the constraints that are common to all keywords (since the |
887 | presence of a coroutine keyword makes the function into a coroutine). */ | |
888 | ||
889 | static bool | |
890 | coro_common_keyword_context_valid_p (tree fndecl, location_t kw_loc, | |
891 | const char *kw_name) | |
892 | { | |
893 | if (fndecl == NULL_TREE) | |
894 | { | |
895 | error_at (kw_loc, "%qs cannot be used outside a function", kw_name); | |
896 | return false; | |
897 | } | |
898 | ||
899 | /* This is arranged in order of prohibitions in the std. */ | |
900 | if (DECL_MAIN_P (fndecl)) | |
901 | { | |
80222642 | 902 | /* [basic.start.main] 3. The function main shall not be a coroutine. */ |
49789fd0 IS |
903 | error_at (kw_loc, "%qs cannot be used in the %<main%> function", |
904 | kw_name); | |
905 | return false; | |
906 | } | |
907 | ||
908 | if (DECL_DECLARED_CONSTEXPR_P (fndecl)) | |
909 | { | |
49789fd0 | 910 | cp_function_chain->invalid_constexpr = true; |
f13d9e48 IS |
911 | if (!is_instantiation_of_constexpr (fndecl)) |
912 | { | |
913 | /* [dcl.constexpr] 3.3 it shall not be a coroutine. */ | |
914 | error_at (kw_loc, "%qs cannot be used in a %<constexpr%> function", | |
915 | kw_name); | |
916 | return false; | |
917 | } | |
49789fd0 IS |
918 | } |
919 | ||
920 | if (FNDECL_USED_AUTO (fndecl)) | |
921 | { | |
80222642 IS |
922 | /* [dcl.spec.auto] 15. A function declared with a return type that uses |
923 | a placeholder type shall not be a coroutine. */ | |
49789fd0 IS |
924 | error_at (kw_loc, |
925 | "%qs cannot be used in a function with a deduced return type", | |
926 | kw_name); | |
927 | return false; | |
928 | } | |
929 | ||
930 | if (varargs_function_p (fndecl)) | |
931 | { | |
80222642 IS |
932 | /* [dcl.fct.def.coroutine] The parameter-declaration-clause of the |
933 | coroutine shall not terminate with an ellipsis that is not part | |
934 | of a parameter-declaration. */ | |
49789fd0 IS |
935 | error_at (kw_loc, |
936 | "%qs cannot be used in a varargs function", kw_name); | |
937 | return false; | |
938 | } | |
939 | ||
940 | if (DECL_CONSTRUCTOR_P (fndecl)) | |
941 | { | |
80222642 | 942 | /* [class.ctor] 7. a constructor shall not be a coroutine. */ |
49789fd0 IS |
943 | error_at (kw_loc, "%qs cannot be used in a constructor", kw_name); |
944 | return false; | |
945 | } | |
946 | ||
947 | if (DECL_DESTRUCTOR_P (fndecl)) | |
948 | { | |
80222642 | 949 | /* [class.dtor] 21. a destructor shall not be a coroutine. */ |
49789fd0 IS |
950 | error_at (kw_loc, "%qs cannot be used in a destructor", kw_name); |
951 | return false; | |
952 | } | |
953 | ||
954 | return true; | |
955 | } | |
956 | ||
957 | /* Here we check the constraints that are not per keyword. */ | |
958 | ||
959 | static bool | |
960 | coro_function_valid_p (tree fndecl) | |
961 | { | |
962 | location_t f_loc = DECL_SOURCE_LOCATION (fndecl); | |
963 | ||
d60c25fa IS |
964 | /* For cases where fundamental information cannot be found, e.g. the |
965 | coroutine traits are missing, we need to punt early. */ | |
966 | if (!coro_promise_type_found_p (fndecl, f_loc)) | |
967 | return false; | |
968 | ||
49789fd0 IS |
969 | /* Since we think the function is a coroutine, that implies we parsed |
970 | a keyword that triggered this. Keywords check promise validity for | |
971 | their context and thus the promise type should be known at this point. */ | |
d60c25fa IS |
972 | if (get_coroutine_handle_type (fndecl) == NULL_TREE |
973 | || get_coroutine_promise_type (fndecl) == NULL_TREE) | |
974 | return false; | |
49789fd0 IS |
975 | |
976 | if (current_function_returns_value || current_function_returns_null) | |
977 | { | |
978 | /* TODO: record or extract positions of returns (and the first coro | |
979 | keyword) so that we can add notes to the diagnostic about where | |
980 | the bad keyword is and what made the function into a coro. */ | |
981 | error_at (f_loc, "a %<return%> statement is not allowed in coroutine;" | |
982 | " did you mean %<co_return%>?"); | |
983 | return false; | |
984 | } | |
985 | ||
986 | return true; | |
987 | } | |
988 | ||
989 | enum suspend_point_kind { | |
990 | CO_AWAIT_SUSPEND_POINT = 0, | |
991 | CO_YIELD_SUSPEND_POINT, | |
992 | INITIAL_SUSPEND_POINT, | |
993 | FINAL_SUSPEND_POINT | |
994 | }; | |
995 | ||
daaed019 IS |
996 | /* Helper function to build a named variable for the temps we use for each |
997 | await point. The root of the name is determined by SUSPEND_KIND, and | |
998 | the variable is of type V_TYPE. The awaitable number is reset each time | |
999 | we encounter a final suspend. */ | |
1000 | ||
1001 | static tree | |
1002 | get_awaitable_var (suspend_point_kind suspend_kind, tree v_type) | |
1003 | { | |
5cca7517 AA |
1004 | auto cinfo = get_coroutine_info (current_function_decl); |
1005 | gcc_checking_assert (cinfo); | |
daaed019 IS |
1006 | char *buf; |
1007 | switch (suspend_kind) | |
1008 | { | |
5cca7517 AA |
1009 | default: buf = xasprintf ("Aw%d", cinfo->awaitable_number++); break; |
1010 | case CO_YIELD_SUSPEND_POINT: | |
1011 | buf = xasprintf ("Yd%d", cinfo->awaitable_number++); | |
1012 | break; | |
1013 | case INITIAL_SUSPEND_POINT: buf = xasprintf ("Is"); break; | |
1014 | case FINAL_SUSPEND_POINT: buf = xasprintf ("Fs"); break; | |
1015 | } | |
daaed019 IS |
1016 | tree ret = get_identifier (buf); |
1017 | free (buf); | |
1018 | ret = build_lang_decl (VAR_DECL, ret, v_type); | |
1019 | DECL_ARTIFICIAL (ret) = true; | |
1020 | return ret; | |
1021 | } | |
1022 | ||
9ee91079 IS |
1023 | /* Helpers to diagnose missing noexcept on final await expressions. */ |
1024 | ||
1025 | static bool | |
1026 | coro_diagnose_throwing_fn (tree fndecl) | |
1027 | { | |
1028 | if (!TYPE_NOTHROW_P (TREE_TYPE (fndecl))) | |
1029 | { | |
cb76fcf5 | 1030 | auto_diagnostic_group d; |
9ee91079 IS |
1031 | location_t f_loc = cp_expr_loc_or_loc (fndecl, |
1032 | DECL_SOURCE_LOCATION (fndecl)); | |
1033 | error_at (f_loc, "the expression %qE is required to be non-throwing", | |
1034 | fndecl); | |
1035 | inform (f_loc, "must be declared with %<noexcept(true)%>"); | |
1036 | return true; | |
1037 | } | |
1038 | return false; | |
1039 | } | |
1040 | ||
1041 | static bool | |
1042 | coro_diagnose_throwing_final_aw_expr (tree expr) | |
1043 | { | |
7b96274a IS |
1044 | if (TREE_CODE (expr) == TARGET_EXPR) |
1045 | expr = TARGET_EXPR_INITIAL (expr); | |
9ee91079 | 1046 | tree fn = NULL_TREE; |
7b96274a IS |
1047 | if (TREE_CODE (expr) == CALL_EXPR) |
1048 | fn = CALL_EXPR_FN (expr); | |
1049 | else if (TREE_CODE (expr) == AGGR_INIT_EXPR) | |
1050 | fn = AGGR_INIT_EXPR_FN (expr); | |
1051 | else if (TREE_CODE (expr) == CONSTRUCTOR) | |
9ee91079 IS |
1052 | return false; |
1053 | else | |
1054 | { | |
1055 | gcc_checking_assert (0 && "unhandled expression type"); | |
1056 | return false; | |
1057 | } | |
1058 | fn = TREE_OPERAND (fn, 0); | |
1059 | return coro_diagnose_throwing_fn (fn); | |
1060 | } | |
1061 | ||
32e678b2 AA |
1062 | /* Build a co_await suitable for later expansion. */ |
1063 | ||
1064 | tree | |
1065 | build_template_co_await_expr (location_t kw, tree type, tree expr, tree kind) | |
1066 | { | |
1067 | tree aw_expr = build5_loc (kw, CO_AWAIT_EXPR, type, expr, | |
1068 | NULL_TREE, NULL_TREE, NULL_TREE, | |
1069 | kind); | |
1070 | TREE_SIDE_EFFECTS (aw_expr) = true; | |
1071 | return aw_expr; | |
1072 | } | |
1073 | ||
1074 | ||
49789fd0 IS |
1075 | /* This performs [expr.await] bullet 3.3 and validates the interface obtained. |
1076 | It is also used to build the initial and final suspend points. | |
1077 | ||
1078 | 'a', 'o' and 'e' are used as per the description in the section noted. | |
1079 | ||
1080 | A, the original yield/await expr, is found at source location LOC. | |
1081 | ||
1082 | We will be constructing a CO_AWAIT_EXPR for a suspend point of one of | |
32e678b2 AA |
1083 | the four suspend_point_kind kinds. This is indicated by SUSPEND_KIND. |
1084 | ||
1085 | In the case that we're processing a template declaration, we can't save | |
1086 | actual awaiter expressions as the frame type will differ between | |
1087 | instantiations, but we can generate them to type-check them and compute the | |
1088 | resulting expression type. In those cases, we will return a | |
1089 | template-appropriate CO_AWAIT_EXPR and throw away the rest of the results. | |
1090 | Such an expression requires the original co_await operand unaltered. Pass | |
1091 | it as ORIG_OPERAND. If it is the same as 'a', you can pass NULL_TREE (the | |
1092 | default) to use 'a' as the value. */ | |
49789fd0 IS |
1093 | |
1094 | static tree | |
32e678b2 AA |
1095 | build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind, |
1096 | tree orig_operand = NULL_TREE) | |
49789fd0 | 1097 | { |
32e678b2 AA |
1098 | if (orig_operand == NULL_TREE) |
1099 | orig_operand = a; | |
1100 | ||
49789fd0 IS |
1101 | /* Try and overload of operator co_await, .... */ |
1102 | tree o; | |
1103 | if (MAYBE_CLASS_TYPE_P (TREE_TYPE (a))) | |
1104 | { | |
49789fd0 | 1105 | o = build_new_op (loc, CO_AWAIT_EXPR, LOOKUP_NORMAL, a, NULL_TREE, |
bb2a7f80 | 1106 | NULL_TREE, NULL_TREE, NULL, tf_warning_or_error); |
49789fd0 IS |
1107 | /* If no viable functions are found, o is a. */ |
1108 | if (!o || o == error_mark_node) | |
1109 | o = a; | |
9ee91079 IS |
1110 | else if (flag_exceptions && suspend_kind == FINAL_SUSPEND_POINT) |
1111 | { | |
1112 | /* We found an overload for co_await(), diagnose throwing cases. */ | |
1113 | if (TREE_CODE (o) == TARGET_EXPR | |
1114 | && coro_diagnose_throwing_final_aw_expr (o)) | |
1115 | return error_mark_node; | |
1116 | ||
1117 | /* We now know that the final suspend object is distinct from the | |
1118 | final awaiter, so check for a non-throwing DTOR where needed. */ | |
44868e72 JM |
1119 | if (tree dummy = cxx_maybe_build_cleanup (a, tf_none)) |
1120 | { | |
1121 | if (CONVERT_EXPR_P (dummy)) | |
1122 | dummy = TREE_OPERAND (dummy, 0); | |
1123 | dummy = TREE_OPERAND (CALL_EXPR_FN (dummy), 0); | |
1124 | if (coro_diagnose_throwing_fn (dummy)) | |
1125 | return error_mark_node; | |
1126 | } | |
9ee91079 | 1127 | } |
49789fd0 IS |
1128 | } |
1129 | else | |
1130 | o = a; /* This is most likely about to fail anyway. */ | |
1131 | ||
a312c801 IS |
1132 | tree o_type = TREE_TYPE (o); |
1133 | if (o_type && !VOID_TYPE_P (o_type)) | |
1134 | o_type = complete_type_or_else (o_type, o); | |
1135 | ||
1136 | if (!o_type) | |
1137 | return error_mark_node; | |
1138 | ||
49789fd0 IS |
1139 | if (TREE_CODE (o_type) != RECORD_TYPE) |
1140 | { | |
1141 | error_at (loc, "awaitable type %qT is not a structure", | |
1142 | o_type); | |
1143 | return error_mark_node; | |
1144 | } | |
1145 | ||
1146 | /* Check for required awaitable members and their types. */ | |
1147 | tree awrd_meth | |
c75199b7 | 1148 | = lookup_awaitable_member (o_type, coro_await_ready_identifier, loc); |
49789fd0 IS |
1149 | if (!awrd_meth || awrd_meth == error_mark_node) |
1150 | return error_mark_node; | |
49789fd0 | 1151 | tree awsp_meth |
c75199b7 | 1152 | = lookup_awaitable_member (o_type, coro_await_suspend_identifier, loc); |
49789fd0 IS |
1153 | if (!awsp_meth || awsp_meth == error_mark_node) |
1154 | return error_mark_node; | |
1155 | ||
1156 | /* The type of the co_await is the return type of the awaitable's | |
c75199b7 | 1157 | await_resume, so we need to look that up. */ |
49789fd0 | 1158 | tree awrs_meth |
c75199b7 | 1159 | = lookup_awaitable_member (o_type, coro_await_resume_identifier, loc); |
49789fd0 IS |
1160 | if (!awrs_meth || awrs_meth == error_mark_node) |
1161 | return error_mark_node; | |
1162 | ||
47dbd69b IS |
1163 | /* [expr.await]/3.3 If o would be a prvalue, the temporary |
1164 | materialization conversion ([conv.rval]) is applied. */ | |
1165 | if (!glvalue_p (o)) | |
1166 | o = get_target_expr (o, tf_warning_or_error); | |
1167 | ||
1168 | tree e_proxy = o; | |
1169 | if (glvalue_p (o)) | |
1170 | o = NULL_TREE; /* Use the existing entity. */ | |
1171 | else /* We need to materialise it. */ | |
daaed019 | 1172 | { |
47dbd69b IS |
1173 | e_proxy = get_awaitable_var (suspend_kind, o_type); |
1174 | o = cp_build_init_expr (loc, e_proxy, o); | |
d0ed0690 | 1175 | e_proxy = convert_from_reference (e_proxy); |
005530eb | 1176 | } |
49789fd0 IS |
1177 | |
1178 | /* I suppose we could check that this is contextually convertible to bool. */ | |
1179 | tree awrd_func = NULL_TREE; | |
1180 | tree awrd_call | |
1181 | = build_new_method_call (e_proxy, awrd_meth, NULL, NULL_TREE, LOOKUP_NORMAL, | |
1182 | &awrd_func, tf_warning_or_error); | |
1183 | ||
1184 | if (!awrd_func || !awrd_call || awrd_call == error_mark_node) | |
1185 | return error_mark_node; | |
1186 | ||
1187 | /* The suspend method may return one of three types: | |
1188 | 1. void (no special action needed). | |
1189 | 2. bool (if true, we don't need to suspend). | |
1190 | 3. a coroutine handle, we execute the handle.resume() call. */ | |
1191 | tree awsp_func = NULL_TREE; | |
1192 | tree h_proxy = get_coroutine_self_handle_proxy (current_function_decl); | |
1193 | vec<tree, va_gc> *args = make_tree_vector_single (h_proxy); | |
1194 | tree awsp_call | |
1195 | = build_new_method_call (e_proxy, awsp_meth, &args, NULL_TREE, | |
1196 | LOOKUP_NORMAL, &awsp_func, tf_warning_or_error); | |
1197 | ||
1198 | release_tree_vector (args); | |
1199 | if (!awsp_func || !awsp_call || awsp_call == error_mark_node) | |
1200 | return error_mark_node; | |
1201 | ||
1202 | bool ok = false; | |
1203 | tree susp_return_type = TREE_TYPE (TREE_TYPE (awsp_func)); | |
1204 | if (same_type_p (susp_return_type, void_type_node)) | |
1205 | ok = true; | |
1206 | else if (same_type_p (susp_return_type, boolean_type_node)) | |
1207 | ok = true; | |
1208 | else if (TREE_CODE (susp_return_type) == RECORD_TYPE | |
8009c79b JEH |
1209 | && CLASS_TYPE_P (susp_return_type) |
1210 | && CLASSTYPE_TEMPLATE_INFO (susp_return_type)) | |
49789fd0 IS |
1211 | { |
1212 | tree tt = CLASSTYPE_TI_TEMPLATE (susp_return_type); | |
1213 | if (tt == coro_handle_templ) | |
1214 | ok = true; | |
1215 | } | |
1216 | ||
1217 | if (!ok) | |
1218 | { | |
1219 | error_at (loc, "%<await_suspend%> must return %<void%>, %<bool%> or" | |
1220 | " a coroutine handle"); | |
1221 | return error_mark_node; | |
1222 | } | |
1223 | ||
1224 | /* Finally, the type of e.await_resume() is the co_await's type. */ | |
1225 | tree awrs_func = NULL_TREE; | |
1226 | tree awrs_call | |
1227 | = build_new_method_call (e_proxy, awrs_meth, NULL, NULL_TREE, LOOKUP_NORMAL, | |
1228 | &awrs_func, tf_warning_or_error); | |
1229 | ||
1230 | if (!awrs_func || !awrs_call || awrs_call == error_mark_node) | |
1231 | return error_mark_node; | |
1232 | ||
9ee91079 IS |
1233 | if (flag_exceptions && suspend_kind == FINAL_SUSPEND_POINT) |
1234 | { | |
1235 | if (coro_diagnose_throwing_fn (awrd_func)) | |
1236 | return error_mark_node; | |
1237 | if (coro_diagnose_throwing_fn (awsp_func)) | |
1238 | return error_mark_node; | |
1239 | if (coro_diagnose_throwing_fn (awrs_func)) | |
1240 | return error_mark_node; | |
44868e72 JM |
1241 | if (tree dummy = cxx_maybe_build_cleanup (e_proxy, tf_none)) |
1242 | { | |
1243 | if (CONVERT_EXPR_P (dummy)) | |
1244 | dummy = TREE_OPERAND (dummy, 0); | |
1245 | dummy = TREE_OPERAND (CALL_EXPR_FN (dummy), 0); | |
1246 | if (coro_diagnose_throwing_fn (dummy)) | |
1247 | return error_mark_node; | |
1248 | } | |
9ee91079 IS |
1249 | } |
1250 | ||
49789fd0 IS |
1251 | /* We now have three call expressions, in terms of the promise, handle and |
1252 | 'e' proxies. Save them in the await expression for later expansion. */ | |
1253 | ||
1254 | tree awaiter_calls = make_tree_vec (3); | |
1255 | TREE_VEC_ELT (awaiter_calls, 0) = awrd_call; /* await_ready(). */ | |
1256 | TREE_VEC_ELT (awaiter_calls, 1) = awsp_call; /* await_suspend(). */ | |
324276ff IS |
1257 | tree te = NULL_TREE; |
1258 | if (TREE_CODE (awrs_call) == TARGET_EXPR) | |
1259 | { | |
1260 | te = awrs_call; | |
1fea6f82 | 1261 | awrs_call = TARGET_EXPR_INITIAL (awrs_call); |
324276ff | 1262 | } |
49789fd0 IS |
1263 | TREE_VEC_ELT (awaiter_calls, 2) = awrs_call; /* await_resume(). */ |
1264 | ||
d0ed0690 JM |
1265 | if (REFERENCE_REF_P (e_proxy)) |
1266 | e_proxy = TREE_OPERAND (e_proxy, 0); | |
1267 | ||
32e678b2 AA |
1268 | tree awrs_type = TREE_TYPE (TREE_TYPE (awrs_func)); |
1269 | tree suspend_kind_cst = build_int_cst (integer_type_node, | |
1270 | (int) suspend_kind); | |
3ef39186 | 1271 | tree await_expr = build5_loc (loc, CO_AWAIT_EXPR, |
32e678b2 | 1272 | awrs_type, |
3ef39186 | 1273 | a, e_proxy, o, awaiter_calls, |
32e678b2 | 1274 | suspend_kind_cst); |
1b54a722 | 1275 | TREE_SIDE_EFFECTS (await_expr) = true; |
324276ff IS |
1276 | if (te) |
1277 | { | |
1278 | TREE_OPERAND (te, 1) = await_expr; | |
1b54a722 | 1279 | TREE_SIDE_EFFECTS (te) = true; |
324276ff IS |
1280 | await_expr = te; |
1281 | } | |
1b54a722 | 1282 | SET_EXPR_LOCATION (await_expr, loc); |
32e678b2 AA |
1283 | |
1284 | if (processing_template_decl) | |
1285 | return build_template_co_await_expr (loc, awrs_type, orig_operand, | |
1286 | suspend_kind_cst); | |
1287 | return convert_from_reference (await_expr); | |
1288 | } | |
1289 | ||
1290 | /* Returns true iff EXPR or the TRAITS_CLASS, which should be a | |
1291 | coroutine_traits instance, are dependent. In those cases, we can't decide | |
1292 | what the types of our co_{await,yield,return} expressions are, so we defer | |
1293 | expansion entirely. */ | |
1294 | ||
1295 | static bool | |
1296 | coro_dependent_p (tree expr, tree traits_class) | |
1297 | { | |
1298 | return type_dependent_expression_p (expr) | |
1299 | || dependent_type_p (traits_class); | |
49789fd0 IS |
1300 | } |
1301 | ||
1302 | tree | |
1303 | finish_co_await_expr (location_t kw, tree expr) | |
1304 | { | |
1305 | if (!expr || error_operand_p (expr)) | |
1306 | return error_mark_node; | |
1307 | ||
1308 | if (!coro_common_keyword_context_valid_p (current_function_decl, kw, | |
1309 | "co_await")) | |
1310 | return error_mark_node; | |
1311 | ||
1312 | /* The current function has now become a coroutine, if it wasn't already. */ | |
1313 | DECL_COROUTINE_P (current_function_decl) = 1; | |
1314 | ||
cf7eac58 IS |
1315 | /* This function will appear to have no return statement, even if it |
1316 | is declared to return non-void (most likely). This is correct - we | |
1317 | synthesize the return for the ramp in the compiler. So suppress any | |
1318 | extraneous warnings during substitution. */ | |
65870e75 | 1319 | suppress_warning (current_function_decl, OPT_Wreturn_type); |
cf7eac58 | 1320 | |
32e678b2 AA |
1321 | /* Prepare for coroutine transformations. */ |
1322 | if (!ensure_coro_initialized (kw)) | |
1323 | return error_mark_node; | |
1324 | ||
1325 | /* Get our traits class. */ | |
1326 | tree traits_class = coro_get_traits_class (current_function_decl, kw); | |
1327 | ||
1328 | /* Defer expansion when we are processing a template, unless the traits type | |
1329 | and expression would not be dependent. In the case that the types are | |
1330 | not dependent but we are processing a template declaration, we will do | |
1331 | most of the computation but throw away the results (except for the | |
1332 | await_resume type). Otherwise, if our co_await is type-dependent | |
1333 | (i.e. the promise type or operand type is dependent), we can't do much, | |
1334 | and just return early with a NULL_TREE type (indicating that we cannot | |
1335 | compute the type yet). */ | |
1336 | if (coro_dependent_p (expr, traits_class)) | |
1337 | return build_template_co_await_expr (kw, NULL_TREE, expr, | |
1338 | integer_zero_node); | |
49789fd0 IS |
1339 | |
1340 | /* We must be able to look up the "await_transform" method in the scope of | |
1341 | the promise type, and obtain its return type. */ | |
1342 | if (!coro_promise_type_found_p (current_function_decl, kw)) | |
1343 | return error_mark_node; | |
1344 | ||
1345 | /* [expr.await] 3.2 | |
1346 | The incoming cast expression might be transformed by a promise | |
1347 | 'await_transform()'. */ | |
1348 | tree at_meth | |
1349 | = lookup_promise_method (current_function_decl, | |
1350 | coro_await_transform_identifier, kw, | |
80222642 | 1351 | /*musthave=*/false); |
49789fd0 IS |
1352 | if (at_meth == error_mark_node) |
1353 | return error_mark_node; | |
1354 | ||
1355 | tree a = expr; | |
1356 | if (at_meth) | |
1357 | { | |
1358 | /* try to build a = p.await_transform (e). */ | |
49789fd0 IS |
1359 | vec<tree, va_gc> *args = make_tree_vector_single (expr); |
1360 | a = build_new_method_call (get_coroutine_promise_proxy ( | |
1361 | current_function_decl), | |
1362 | at_meth, &args, NULL_TREE, LOOKUP_NORMAL, | |
3cf2a9e0 | 1363 | NULL, tf_warning_or_error); |
49789fd0 IS |
1364 | |
1365 | /* As I read the section. | |
1366 | We saw an await_transform method, so it's mandatory that we replace | |
1367 | expr with p.await_transform (expr), therefore if the method call fails | |
1368 | (presumably, we don't have suitable arguments) then this part of the | |
1369 | process fails. */ | |
3cf2a9e0 | 1370 | if (a == error_mark_node) |
49789fd0 IS |
1371 | return error_mark_node; |
1372 | } | |
1373 | ||
1374 | /* Now we want to build co_await a. */ | |
32e678b2 | 1375 | return build_co_await (kw, a, CO_AWAIT_SUSPEND_POINT, expr); |
49789fd0 IS |
1376 | } |
1377 | ||
1378 | /* Take the EXPR given and attempt to build: | |
1379 | co_await p.yield_value (expr); | |
1380 | per [expr.yield] para 1. */ | |
1381 | ||
1382 | tree | |
1383 | finish_co_yield_expr (location_t kw, tree expr) | |
1384 | { | |
1385 | if (!expr || error_operand_p (expr)) | |
1386 | return error_mark_node; | |
1387 | ||
1388 | /* Check the general requirements and simple syntax errors. */ | |
1389 | if (!coro_common_keyword_context_valid_p (current_function_decl, kw, | |
1390 | "co_yield")) | |
1391 | return error_mark_node; | |
1392 | ||
1393 | /* The current function has now become a coroutine, if it wasn't already. */ | |
1394 | DECL_COROUTINE_P (current_function_decl) = 1; | |
1395 | ||
cf7eac58 IS |
1396 | /* This function will appear to have no return statement, even if it |
1397 | is declared to return non-void (most likely). This is correct - we | |
1398 | synthesize the return for the ramp in the compiler. So suppress any | |
1399 | extraneous warnings during substitution. */ | |
65870e75 | 1400 | suppress_warning (current_function_decl, OPT_Wreturn_type); |
cf7eac58 | 1401 | |
32e678b2 AA |
1402 | /* Prepare for coroutine transformations. */ |
1403 | if (!ensure_coro_initialized (kw)) | |
1404 | return error_mark_node; | |
1405 | ||
1406 | /* Get our traits class. */ | |
1407 | tree traits_class = coro_get_traits_class (current_function_decl, kw); | |
1408 | ||
1409 | /* Defer expansion when we are processing a template; see note in | |
1410 | finish_co_await_expr. Also note that a yield is equivalent to | |
1411 | ||
1412 | co_await p.yield_value(EXPR) | |
1413 | ||
1414 | If either p or EXPR are type-dependent, then the whole expression is | |
1415 | certainly type-dependent, and we can't proceed. */ | |
1416 | if (coro_dependent_p (expr, traits_class)) | |
1417 | return build2_loc (kw, CO_YIELD_EXPR, NULL_TREE, expr, NULL_TREE); | |
49789fd0 IS |
1418 | |
1419 | if (!coro_promise_type_found_p (current_function_decl, kw)) | |
1420 | /* We must be able to look up the "yield_value" method in the scope of | |
1421 | the promise type, and obtain its return type. */ | |
1422 | return error_mark_node; | |
1423 | ||
3cf2a9e0 IS |
1424 | /* [expr.yield] / 1 |
1425 | Let e be the operand of the yield-expression and p be an lvalue naming | |
1426 | the promise object of the enclosing coroutine, then the yield-expression | |
1427 | is equivalent to the expression co_await p.yield_value(e). | |
1428 | build p.yield_value(e): */ | |
49789fd0 | 1429 | vec<tree, va_gc> *args = make_tree_vector_single (expr); |
e74c7607 IS |
1430 | tree yield_call |
1431 | = coro_build_promise_expression (current_function_decl, NULL, | |
1432 | coro_yield_value_identifier, kw, | |
1433 | &args, /*musthave=*/true); | |
1434 | release_tree_vector (args); | |
49789fd0 | 1435 | |
3cf2a9e0 | 1436 | /* Now build co_await p.yield_value (e). |
49789fd0 | 1437 | Noting that for co_yield, there is no evaluation of any potential |
3cf2a9e0 | 1438 | promise transform_await(), so we call build_co_await directly. */ |
49789fd0 IS |
1439 | |
1440 | tree op = build_co_await (kw, yield_call, CO_YIELD_SUSPEND_POINT); | |
6ac6529e J |
1441 | if (op != error_mark_node) |
1442 | { | |
324276ff IS |
1443 | if (REFERENCE_REF_P (op)) |
1444 | op = TREE_OPERAND (op, 0); | |
1445 | /* If the await expression is wrapped in a TARGET_EXPR, then transfer | |
1446 | that wrapper to the CO_YIELD_EXPR, since this is just a proxy for | |
1447 | its contained await. Otherwise, just build the CO_YIELD_EXPR. */ | |
1448 | if (TREE_CODE (op) == TARGET_EXPR) | |
1449 | { | |
1fea6f82 | 1450 | tree t = TARGET_EXPR_INITIAL (op); |
324276ff | 1451 | t = build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (t), expr, t); |
1fea6f82 | 1452 | TARGET_EXPR_INITIAL (op) = t; |
324276ff IS |
1453 | } |
1454 | else | |
1455 | op = build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (op), expr, op); | |
6ac6529e | 1456 | TREE_SIDE_EFFECTS (op) = 1; |
324276ff | 1457 | op = convert_from_reference (op); |
6ac6529e | 1458 | } |
49789fd0 IS |
1459 | |
1460 | return op; | |
1461 | } | |
1462 | ||
a2086f98 | 1463 | /* Check and build a co_return statement. |
5ef067eb | 1464 | First that it's valid to have a co_return keyword here. |
49789fd0 | 1465 | If it is, then check and build the p.return_{void(),value(expr)}. |
5ef067eb IS |
1466 | These are built against a proxy for the promise, which will be filled |
1467 | in with the actual frame version when the function is transformed. */ | |
49789fd0 IS |
1468 | |
1469 | tree | |
1470 | finish_co_return_stmt (location_t kw, tree expr) | |
1471 | { | |
5ef067eb IS |
1472 | if (expr) |
1473 | STRIP_ANY_LOCATION_WRAPPER (expr); | |
1474 | ||
1475 | if (error_operand_p (expr)) | |
49789fd0 IS |
1476 | return error_mark_node; |
1477 | ||
5ef067eb IS |
1478 | /* If it fails the following test, the function is not permitted to be a |
1479 | coroutine, so the co_return statement is erroneous. */ | |
49789fd0 IS |
1480 | if (!coro_common_keyword_context_valid_p (current_function_decl, kw, |
1481 | "co_return")) | |
1482 | return error_mark_node; | |
1483 | ||
1484 | /* The current function has now become a coroutine, if it wasn't | |
1485 | already. */ | |
1486 | DECL_COROUTINE_P (current_function_decl) = 1; | |
1487 | ||
5ef067eb IS |
1488 | /* This function will appear to have no return statement, even if it |
1489 | is declared to return non-void (most likely). This is correct - we | |
1490 | synthesize the return for the ramp in the compiler. So suppress any | |
1491 | extraneous warnings during substitution. */ | |
65870e75 | 1492 | suppress_warning (current_function_decl, OPT_Wreturn_type); |
49789fd0 | 1493 | |
32e678b2 AA |
1494 | /* Prepare for coroutine transformations. */ |
1495 | if (!ensure_coro_initialized (kw)) | |
1496 | return error_mark_node; | |
1497 | ||
1498 | /* Get our traits class. */ | |
1499 | tree traits_class = coro_get_traits_class (current_function_decl, kw); | |
1500 | ||
5ef067eb IS |
1501 | if (processing_template_decl |
1502 | && check_for_bare_parameter_packs (expr)) | |
1503 | return error_mark_node; | |
49789fd0 | 1504 | |
32e678b2 AA |
1505 | /* Defer expansion when we must and are processing a template; see note in |
1506 | finish_co_await_expr. */ | |
1507 | if (coro_dependent_p (expr, traits_class)) | |
5ef067eb IS |
1508 | { |
1509 | /* co_return expressions are always void type, regardless of the | |
1510 | expression type. */ | |
1511 | expr = build2_loc (kw, CO_RETURN_EXPR, void_type_node, | |
1512 | expr, NULL_TREE); | |
1513 | expr = maybe_cleanup_point_expr_void (expr); | |
1514 | return add_stmt (expr); | |
49789fd0 IS |
1515 | } |
1516 | ||
1517 | if (!coro_promise_type_found_p (current_function_decl, kw)) | |
1518 | return error_mark_node; | |
1519 | ||
49789fd0 IS |
1520 | /* Suppress -Wreturn-type for co_return, we need to check indirectly |
1521 | whether the promise type has a suitable return_void/return_value. */ | |
65870e75 | 1522 | suppress_warning (current_function_decl, OPT_Wreturn_type); |
49789fd0 IS |
1523 | |
1524 | if (!processing_template_decl && warn_sequence_point) | |
1525 | verify_sequence_points (expr); | |
1526 | ||
5ef067eb IS |
1527 | if (expr) |
1528 | { | |
1529 | /* If we had an id-expression obfuscated by force_paren_expr, we need | |
1530 | to undo it so we can try to treat it as an rvalue below. */ | |
1531 | expr = maybe_undo_parenthesized_ref (expr); | |
1532 | ||
5ef067eb IS |
1533 | if (error_operand_p (expr)) |
1534 | return error_mark_node; | |
1535 | } | |
1536 | ||
49789fd0 IS |
1537 | /* If the promise object doesn't have the correct return call then |
1538 | there's a mis-match between the co_return <expr> and this. */ | |
5ef067eb | 1539 | tree co_ret_call = error_mark_node; |
49789fd0 | 1540 | if (expr == NULL_TREE || VOID_TYPE_P (TREE_TYPE (expr))) |
e74c7607 IS |
1541 | co_ret_call |
1542 | = get_coroutine_return_void_expr (current_function_decl, kw, true); | |
49789fd0 IS |
1543 | else |
1544 | { | |
5ef067eb IS |
1545 | /* [class.copy.elision] / 3. |
1546 | An implicitly movable entity is a variable of automatic storage | |
1547 | duration that is either a non-volatile object or an rvalue reference | |
1548 | to a non-volatile object type. For such objects in the context of | |
1549 | the co_return, the overload resolution should be carried out first | |
1550 | treating the object as an rvalue, if that fails, then we fall back | |
1551 | to regular overload resolution. */ | |
1552 | ||
1722e201 JM |
1553 | tree arg = expr; |
1554 | if (tree moved = treat_lvalue_as_rvalue_p (expr, /*return*/true)) | |
1555 | arg = moved; | |
1556 | ||
1557 | releasing_vec args = make_tree_vector_single (arg); | |
1558 | co_ret_call | |
1559 | = coro_build_promise_expression (current_function_decl, NULL, | |
1560 | coro_return_value_identifier, kw, | |
1561 | &args, /*musthave=*/true); | |
49789fd0 IS |
1562 | } |
1563 | ||
1564 | /* Makes no sense for a co-routine really. */ | |
1565 | if (TREE_THIS_VOLATILE (current_function_decl)) | |
1566 | warning_at (kw, 0, | |
1567 | "function declared %<noreturn%> has a" | |
1568 | " %<co_return%> statement"); | |
1569 | ||
49789fd0 | 1570 | expr = build2_loc (kw, CO_RETURN_EXPR, void_type_node, expr, co_ret_call); |
f898793b IS |
1571 | expr = maybe_cleanup_point_expr_void (expr); |
1572 | return add_stmt (expr); | |
49789fd0 IS |
1573 | } |
1574 | ||
1575 | /* We need to validate the arguments to __builtin_coro_promise, since the | |
1576 | second two must be constant, and the builtins machinery doesn't seem to | |
1577 | deal with that properly. */ | |
1578 | ||
1579 | tree | |
1580 | coro_validate_builtin_call (tree call, tsubst_flags_t) | |
1581 | { | |
1582 | tree fn = TREE_OPERAND (CALL_EXPR_FN (call), 0); | |
1583 | ||
1584 | gcc_checking_assert (DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL); | |
1585 | switch (DECL_FUNCTION_CODE (fn)) | |
1586 | { | |
1587 | default: | |
1588 | return call; | |
1589 | ||
1590 | case BUILT_IN_CORO_PROMISE: | |
1591 | { | |
1592 | /* Argument 0 is already checked by the normal built-in machinery | |
1593 | Argument 1 must be a constant of size type. It probably makes | |
1594 | little sense if it's not a power of 2, but that isn't specified | |
1595 | formally. */ | |
1596 | tree arg = CALL_EXPR_ARG (call, 1); | |
1597 | location_t loc = EXPR_LOCATION (arg); | |
1598 | ||
1599 | /* We expect alignof expressions in templates. */ | |
dad31187 | 1600 | if (TREE_CODE (arg) == ALIGNOF_EXPR) |
49789fd0 IS |
1601 | ; |
1602 | else if (!TREE_CONSTANT (arg)) | |
1603 | { | |
1604 | error_at (loc, "the align argument to %<__builtin_coro_promise%>" | |
1605 | " must be a constant"); | |
1606 | return error_mark_node; | |
1607 | } | |
1608 | /* Argument 2 is the direction - to / from handle address to promise | |
1609 | address. */ | |
1610 | arg = CALL_EXPR_ARG (call, 2); | |
1611 | loc = EXPR_LOCATION (arg); | |
1612 | if (!TREE_CONSTANT (arg)) | |
1613 | { | |
1614 | error_at (loc, "the direction argument to" | |
1615 | " %<__builtin_coro_promise%> must be a constant"); | |
1616 | return error_mark_node; | |
1617 | } | |
1618 | return call; | |
1619 | break; | |
1620 | } | |
1621 | } | |
1622 | } | |
1623 | ||
1624 | /* ================= Morph and Expand. ================= | |
1625 | ||
1626 | The entry point here is morph_fn_to_coro () which is called from | |
1627 | finish_function () when we have completed any template expansion. | |
1628 | ||
1629 | This is preceded by helper functions that implement the phases below. | |
1630 | ||
1631 | The process proceeds in four phases. | |
1632 | ||
1633 | A Initial framing. | |
1634 | The user's function body is wrapped in the initial and final suspend | |
1635 | points and we begin building the coroutine frame. | |
1636 | We build empty decls for the actor and destroyer functions at this | |
1637 | time too. | |
1638 | When exceptions are enabled, the user's function body will also be | |
1639 | wrapped in a try-catch block with the catch invoking the promise | |
1640 | class 'unhandled_exception' method. | |
1641 | ||
1642 | B Analysis. | |
1643 | The user's function body is analyzed to determine the suspend points, | |
1644 | if any, and to capture local variables that might persist across such | |
1645 | suspensions. In most cases, it is not necessary to capture compiler | |
1646 | temporaries, since the tree-lowering nests the suspensions correctly. | |
1647 | However, in the case of a captured reference, there is a lifetime | |
1648 | extension to the end of the full expression - which can mean across a | |
1649 | suspend point in which case it must be promoted to a frame variable. | |
1650 | ||
1651 | At the conclusion of analysis, we have a conservative frame layout and | |
1652 | maps of the local variables to their frame entry points. | |
1653 | ||
1654 | C Build the ramp function. | |
1655 | Carry out the allocation for the coroutine frame (NOTE; the actual size | |
1656 | computation is deferred until late in the middle end to allow for future | |
1657 | optimizations that will be allowed to elide unused frame entries). | |
1658 | We build the return object. | |
1659 | ||
1660 | D Build and expand the actor and destroyer function bodies. | |
1661 | The destroyer is a trivial shim that sets a bit to indicate that the | |
1662 | destroy dispatcher should be used and then calls into the actor. | |
1663 | ||
1664 | The actor function is the implementation of the user's state machine. | |
1665 | The current suspend point is noted in an index. | |
1666 | Each suspend point is encoded as a pair of internal functions, one in | |
1667 | the relevant dispatcher, and one representing the suspend point. | |
1668 | ||
1669 | During this process, the user's local variables and the proxies for the | |
1670 | self-handle and the promise class instance are re-written to their | |
1671 | coroutine frame equivalents. | |
1672 | ||
1673 | The complete bodies for the ramp, actor and destroy function are passed | |
1674 | back to finish_function for folding and gimplification. */ | |
1675 | ||
049a927c IS |
1676 | /* Helper to build a coroutine state frame access expression |
1677 | CORO_FP is a frame pointer | |
1678 | MEMBER_ID is an identifier for a frame field | |
1679 | PRESERVE_REF is true, the expression returned will have REFERENCE_TYPE if | |
1680 | the MEMBER does. | |
1681 | COMPLAIN is passed to the underlying functions. */ | |
1682 | ||
1683 | static tree | |
1684 | coro_build_frame_access_expr (tree coro_ref, tree member_id, bool preserve_ref, | |
1685 | tsubst_flags_t complain) | |
1686 | { | |
1687 | gcc_checking_assert (INDIRECT_REF_P (coro_ref)); | |
1688 | tree fr_type = TREE_TYPE (coro_ref); | |
1689 | tree mb = lookup_member (fr_type, member_id, /*protect=*/1, /*want_type=*/0, | |
1690 | complain); | |
1691 | if (!mb || mb == error_mark_node) | |
1692 | return error_mark_node; | |
1693 | tree expr | |
1694 | = build_class_member_access_expr (coro_ref, mb, NULL_TREE, | |
1695 | preserve_ref, complain); | |
1696 | return expr; | |
1697 | } | |
1698 | ||
49789fd0 IS |
1699 | /* Helpers to build EXPR_STMT and void-cast EXPR_STMT, common ops. */ |
1700 | ||
1701 | static tree | |
1702 | coro_build_expr_stmt (tree expr, location_t loc) | |
1703 | { | |
1704 | return maybe_cleanup_point_expr_void (build_stmt (loc, EXPR_STMT, expr)); | |
1705 | } | |
1706 | ||
1707 | static tree | |
1708 | coro_build_cvt_void_expr_stmt (tree expr, location_t loc) | |
1709 | { | |
1710 | tree t = build1 (CONVERT_EXPR, void_type_node, expr); | |
1711 | return coro_build_expr_stmt (t, loc); | |
1712 | } | |
1713 | ||
a45a7ecd IS |
1714 | /* Helpers to build an artificial var, with location LOC, NAME and TYPE, in |
1715 | CTX, and with initializer INIT. */ | |
1716 | ||
1717 | static tree | |
1718 | coro_build_artificial_var (location_t loc, tree name, tree type, tree ctx, | |
1719 | tree init) | |
1720 | { | |
1721 | tree res = build_lang_decl (VAR_DECL, name, type); | |
1722 | DECL_SOURCE_LOCATION (res) = loc; | |
1723 | DECL_CONTEXT (res) = ctx; | |
1724 | DECL_ARTIFICIAL (res) = true; | |
1725 | DECL_INITIAL (res) = init; | |
1726 | return res; | |
1727 | } | |
1728 | ||
cbe1b1c6 IS |
1729 | /* As above, except allowing the name to be a string. */ |
1730 | ||
a45a7ecd IS |
1731 | static tree |
1732 | coro_build_artificial_var (location_t loc, const char *name, tree type, | |
1733 | tree ctx, tree init) | |
1734 | { | |
1735 | return coro_build_artificial_var (loc, get_identifier (name), | |
1736 | type, ctx, init); | |
1737 | } | |
1738 | ||
cbe1b1c6 IS |
1739 | /* As for coro_build_artificial_var, except that we push this decl into |
1740 | the current binding scope and add the decl_expr. */ | |
1741 | ||
1742 | static tree | |
1743 | coro_build_and_push_artificial_var (location_t loc, const char *name, | |
1744 | tree type, tree ctx, tree init) | |
1745 | { | |
1746 | tree v = coro_build_artificial_var (loc, name, type, ctx, init); | |
1747 | v = pushdecl (v); | |
1748 | add_decl_expr (v); | |
1749 | return v; | |
1750 | } | |
1751 | ||
1752 | /* Build a var NAME of TYPE in CTX and with INIT; add a DECL_VALUE_EXPR that | |
1753 | refers to BASE.FIELD. */ | |
1754 | ||
1755 | static tree | |
1756 | coro_build_artificial_var_with_dve (location_t loc, tree name, tree type, | |
1757 | tree ctx, tree init, tree base, | |
1758 | tree field = NULL_TREE) | |
1759 | { | |
1760 | tree v = coro_build_artificial_var (loc, name, type, ctx, init); | |
1761 | if (field == NULL_TREE) | |
1762 | field = name; | |
1763 | tree dve = coro_build_frame_access_expr (base, field, true, | |
1764 | tf_warning_or_error); | |
1765 | SET_DECL_VALUE_EXPR (v, dve); | |
1766 | DECL_HAS_VALUE_EXPR_P (v) = true; | |
1767 | return v; | |
1768 | } | |
1769 | ||
1770 | /* As for coro_build_artificial_var_with_dve, but push into the current binding | |
1771 | scope and add the decl_expr. */ | |
1772 | ||
1773 | static tree | |
1774 | coro_build_and_push_artificial_var_with_dve (location_t loc, tree name, | |
1775 | tree type, tree ctx, tree init, | |
1776 | tree base, tree field = NULL_TREE) | |
1777 | { | |
1778 | tree v = coro_build_artificial_var_with_dve (loc, name, type, ctx, init, | |
1779 | base, field); | |
1780 | v = pushdecl (v); | |
1781 | add_decl_expr (v); | |
1782 | return v; | |
1783 | } | |
49789fd0 IS |
1784 | |
1785 | /* 2. Create a named label in the specified context. */ | |
1786 | ||
1787 | static tree | |
1788 | create_named_label_with_ctx (location_t loc, const char *name, tree ctx) | |
1789 | { | |
1790 | tree lab_id = get_identifier (name); | |
1791 | tree lab = define_label (loc, lab_id); | |
1792 | DECL_CONTEXT (lab) = ctx; | |
1793 | DECL_ARTIFICIAL (lab) = true; | |
1794 | TREE_USED (lab) = true; | |
1795 | return lab; | |
1796 | } | |
1797 | ||
1798 | struct proxy_replace | |
1799 | { | |
1800 | tree from, to; | |
1801 | }; | |
1802 | ||
1803 | static tree | |
1804 | replace_proxy (tree *here, int *do_subtree, void *d) | |
1805 | { | |
1806 | proxy_replace *data = (proxy_replace *) d; | |
1807 | ||
1808 | if (*here == data->from) | |
1809 | { | |
1810 | *here = data->to; | |
1811 | *do_subtree = 0; | |
1812 | } | |
1813 | else | |
1814 | *do_subtree = 1; | |
1815 | return NULL_TREE; | |
1816 | } | |
1817 | ||
49789fd0 IS |
1818 | /* Support for expansion of co_await statements. */ |
1819 | ||
1820 | struct coro_aw_data | |
1821 | { | |
1822 | tree actor_fn; /* Decl for context. */ | |
1823 | tree coro_fp; /* Frame pointer var. */ | |
1824 | tree resume_idx; /* This is the index var in the frame. */ | |
2a1f0f64 | 1825 | tree i_a_r_c; /* initial suspend await_resume() was called if true. */ |
49789fd0 IS |
1826 | tree self_h; /* This is a handle to the current coro (frame var). */ |
1827 | tree cleanup; /* This is where to go once we complete local destroy. */ | |
1828 | tree cororet; /* This is where to go if we suspend. */ | |
6d85947d | 1829 | tree corocont; /* This is where to go if we continue. */ |
368ba7ae | 1830 | tree dispatch; /* This is where we go if we restart the dispatch. */ |
6d85947d | 1831 | tree conthand; /* This is the handle for a continuation. */ |
49789fd0 IS |
1832 | unsigned index; /* This is our current resume index. */ |
1833 | }; | |
1834 | ||
a2086f98 | 1835 | /* Lightweight search for the first await expression in tree-walk order. |
0666767e IS |
1836 | returns: |
1837 | The first await expression found in STMT. | |
1838 | NULL_TREE if there are none. | |
1839 | So can be used to determine if the statement needs to be processed for | |
1840 | awaits. */ | |
1841 | ||
49789fd0 | 1842 | static tree |
0f66b848 | 1843 | co_await_find_in_subtree (tree *stmt, int *, void *d) |
49789fd0 IS |
1844 | { |
1845 | tree **p = (tree **) d; | |
1846 | if (TREE_CODE (*stmt) == CO_AWAIT_EXPR) | |
1847 | { | |
1848 | *p = stmt; | |
1849 | return *stmt; | |
1850 | } | |
1851 | return NULL_TREE; | |
1852 | } | |
1853 | ||
a2086f98 | 1854 | /* Starting with a statement: |
49789fd0 | 1855 | |
0666767e | 1856 | stmt => some tree containing one or more await expressions. |
49789fd0 | 1857 | |
0666767e IS |
1858 | We replace the statement with: |
1859 | <STATEMENT_LIST> { | |
a2086f98 | 1860 | initialize awaitable |
0666767e IS |
1861 | if (!ready) |
1862 | { | |
1863 | suspension context. | |
1864 | } | |
1865 | resume: | |
1866 | revised statement with one await expression rewritten to its | |
1867 | await_resume() return value. | |
1868 | } | |
1869 | ||
1870 | We then recurse into the initializer and the revised statement | |
1871 | repeating this replacement until there are no more await expressions | |
1872 | in either. */ | |
49789fd0 | 1873 | |
0666767e | 1874 | static tree * |
368ba7ae | 1875 | expand_one_await_expression (tree *expr, tree *await_expr, void *d) |
0666767e | 1876 | { |
49789fd0 | 1877 | coro_aw_data *data = (coro_aw_data *) d; |
49789fd0 | 1878 | |
368ba7ae | 1879 | tree saved_statement = *expr; |
0666767e | 1880 | tree saved_co_await = *await_expr; |
49789fd0 IS |
1881 | |
1882 | tree actor = data->actor_fn; | |
368ba7ae | 1883 | location_t loc = EXPR_LOCATION (*expr); |
49789fd0 | 1884 | tree var = TREE_OPERAND (saved_co_await, 1); /* frame slot. */ |
368ba7ae | 1885 | tree init_expr = TREE_OPERAND (saved_co_await, 2); /* initializer. */ |
49789fd0 IS |
1886 | tree awaiter_calls = TREE_OPERAND (saved_co_await, 3); |
1887 | ||
1888 | tree source = TREE_OPERAND (saved_co_await, 4); | |
1889 | bool is_final = (source | |
1890 | && TREE_INT_CST_LOW (source) == (int) FINAL_SUSPEND_POINT); | |
368ba7ae IS |
1891 | |
1892 | /* Build labels for the destinations of the control flow when we are resuming | |
1893 | or destroying. */ | |
49789fd0 | 1894 | int resume_point = data->index; |
368ba7ae | 1895 | char *buf = xasprintf ("destroy.%d", resume_point); |
49789fd0 | 1896 | tree destroy_label = create_named_label_with_ctx (loc, buf, actor); |
368ba7ae IS |
1897 | free (buf); |
1898 | buf = xasprintf ("resume.%d", resume_point); | |
49789fd0 | 1899 | tree resume_label = create_named_label_with_ctx (loc, buf, actor); |
368ba7ae | 1900 | free (buf); |
49789fd0 | 1901 | |
368ba7ae IS |
1902 | /* This will contain our expanded expression and replace the original |
1903 | expression. */ | |
1904 | tree stmt_list = push_stmt_list (); | |
0666767e | 1905 | tree *await_init = NULL; |
daaed019 | 1906 | |
368ba7ae IS |
1907 | bool needs_dtor = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (var)); |
1908 | if (!init_expr) | |
daaed019 | 1909 | needs_dtor = false; /* No need, the var's lifetime is managed elsewhere. */ |
005530eb IS |
1910 | else |
1911 | { | |
368ba7ae | 1912 | finish_expr_stmt (init_expr); |
0666767e IS |
1913 | /* We have an initializer, which might itself contain await exprs. */ |
1914 | await_init = tsi_stmt_ptr (tsi_last (stmt_list)); | |
005530eb | 1915 | } |
49789fd0 IS |
1916 | |
1917 | /* Use the await_ready() call to test if we need to suspend. */ | |
1918 | tree ready_cond = TREE_VEC_ELT (awaiter_calls, 0); /* await_ready(). */ | |
541840b8 IS |
1919 | /* Convert to bool, if necessary. */ |
1920 | if (TREE_CODE (TREE_TYPE (ready_cond)) != BOOLEAN_TYPE) | |
1921 | ready_cond = cp_convert (boolean_type_node, ready_cond, | |
1922 | tf_warning_or_error); | |
368ba7ae | 1923 | tree susp_if = begin_if_stmt (); |
541840b8 IS |
1924 | /* Be aggressive in folding here, since there are a significant number of |
1925 | cases where the ready condition is constant. */ | |
1926 | ready_cond = invert_truthvalue_loc (loc, ready_cond); | |
368ba7ae | 1927 | finish_if_stmt_cond (ready_cond, susp_if); |
49789fd0 | 1928 | |
49789fd0 | 1929 | tree susp_idx = build_int_cst (short_unsigned_type_node, data->index); |
368ba7ae IS |
1930 | tree r = cp_build_init_expr (data->resume_idx, susp_idx); |
1931 | finish_expr_stmt (r); | |
49789fd0 | 1932 | |
6d85947d IS |
1933 | /* Find out what we have to do with the awaiter's suspend method. |
1934 | [expr.await] | |
1935 | (5.1) If the result of await-ready is false, the coroutine is considered | |
1936 | suspended. Then: | |
1937 | (5.1.1) If the type of await-suspend is std::coroutine_handle<Z>, | |
1938 | await-suspend.resume() is evaluated. | |
1939 | (5.1.2) if the type of await-suspend is bool, await-suspend is evaluated, | |
1940 | and the coroutine is resumed if the result is false. | |
1941 | (5.1.3) Otherwise, await-suspend is evaluated. */ | |
1942 | ||
49789fd0 | 1943 | tree suspend = TREE_VEC_ELT (awaiter_calls, 1); /* await_suspend(). */ |
6d85947d | 1944 | tree susp_type = TREE_TYPE (suspend); |
49789fd0 | 1945 | |
6d85947d IS |
1946 | bool is_cont = false; |
1947 | /* NOTE: final suspend can't resume; the "resume" label in that case | |
1948 | corresponds to implicit destruction. */ | |
1949 | if (VOID_TYPE_P (susp_type)) | |
368ba7ae IS |
1950 | /* Void return - just proceed to suspend. */ |
1951 | finish_expr_stmt (suspend); | |
6d85947d | 1952 | else if (TREE_CODE (susp_type) == BOOLEAN_TYPE) |
49789fd0 | 1953 | { |
368ba7ae IS |
1954 | /* Boolean return, "continue" if the call returns false. */ |
1955 | tree restart_if = begin_if_stmt (); | |
1956 | suspend = invert_truthvalue_loc (loc, suspend); | |
1957 | finish_if_stmt_cond (suspend, restart_if); | |
1958 | /* Resume - so restart the dispatcher, since we do not know if this | |
1959 | coroutine was already resumed from within await_suspend. We must | |
1960 | exit this scope without cleanups. */ | |
1961 | r = build_call_expr_internal_loc (loc, IFN_CO_SUSPN, void_type_node, 1, | |
1962 | build_address (data->dispatch)); | |
1963 | /* This will eventually expand to 'goto coro.restart.dispatch'. */ | |
1964 | finish_expr_stmt (r); | |
1965 | finish_then_clause (restart_if); | |
1966 | finish_if_stmt (restart_if); | |
49789fd0 IS |
1967 | } |
1968 | else | |
1969 | { | |
368ba7ae | 1970 | /* Handle return, save it to the continuation. */ |
0143b277 JM |
1971 | r = suspend; |
1972 | if (!same_type_ignoring_top_level_qualifiers_p (susp_type, | |
1973 | void_coro_handle_type)) | |
1974 | r = build1_loc (loc, VIEW_CONVERT_EXPR, void_coro_handle_type, r); | |
6ffbf87c | 1975 | r = cp_build_init_expr (loc, data->conthand, r); |
368ba7ae | 1976 | finish_expr_stmt (r); |
6d85947d IS |
1977 | is_cont = true; |
1978 | } | |
1979 | ||
1980 | tree d_l = build_address (destroy_label); | |
1981 | tree r_l = build_address (resume_label); | |
1982 | tree susp = build_address (data->cororet); | |
1983 | tree cont = build_address (data->corocont); | |
49789fd0 IS |
1984 | tree final_susp = build_int_cst (integer_type_node, is_final ? 1 : 0); |
1985 | ||
1986 | susp_idx = build_int_cst (integer_type_node, data->index); | |
1987 | ||
1988 | tree sw = begin_switch_stmt (); | |
49789fd0 IS |
1989 | |
1990 | r = build_call_expr_internal_loc (loc, IFN_CO_YIELD, integer_type_node, 5, | |
1991 | susp_idx, final_susp, r_l, d_l, | |
1992 | data->coro_fp); | |
49789fd0 | 1993 | finish_switch_cond (r, sw); |
ff0cba20 | 1994 | finish_case_label (loc, integer_zero_node, NULL_TREE); /* case 0: */ |
49789fd0 | 1995 | /* Implement the suspend, a scope exit without clean ups. */ |
6d85947d IS |
1996 | r = build_call_expr_internal_loc (loc, IFN_CO_SUSPN, void_type_node, 1, |
1997 | is_cont ? cont : susp); | |
368ba7ae | 1998 | finish_expr_stmt (r); /* This will eventually expand to 'goto return'. */ |
ff0cba20 | 1999 | finish_case_label (loc, integer_one_node, NULL_TREE); /* case 1: */ |
368ba7ae | 2000 | add_stmt (build_stmt (loc, GOTO_EXPR, resume_label)); /* goto resume; */ |
ff0cba20 | 2001 | finish_case_label (loc, NULL_TREE, NULL_TREE); /* default:; */ |
368ba7ae | 2002 | add_stmt (build_stmt (loc, GOTO_EXPR, destroy_label)); /* goto destroy; */ |
49789fd0 | 2003 | |
368ba7ae IS |
2004 | finish_switch_stmt (sw); |
2005 | add_stmt (build_stmt (loc, LABEL_EXPR, destroy_label)); | |
49789fd0 | 2006 | if (needs_dtor) |
368ba7ae IS |
2007 | finish_expr_stmt (build_cleanup (var)); |
2008 | add_stmt (build_stmt (loc, GOTO_EXPR, data->cleanup)); | |
49789fd0 | 2009 | |
368ba7ae IS |
2010 | finish_then_clause (susp_if); |
2011 | finish_if_stmt (susp_if); | |
49789fd0 IS |
2012 | |
2013 | /* Resume point. */ | |
368ba7ae | 2014 | add_stmt (build_stmt (loc, LABEL_EXPR, resume_label)); |
49789fd0 IS |
2015 | |
2016 | /* This will produce the value (if one is provided) from the co_await | |
2017 | expression. */ | |
2018 | tree resume_call = TREE_VEC_ELT (awaiter_calls, 2); /* await_resume(). */ | |
3ef39186 J |
2019 | if (REFERENCE_REF_P (resume_call)) |
2020 | /* Sink to await_resume call_expr. */ | |
2021 | resume_call = TREE_OPERAND (resume_call, 0); | |
0666767e IS |
2022 | |
2023 | *await_expr = resume_call; /* Replace the co_await expr with its result. */ | |
2024 | append_to_statement_list_force (saved_statement, &stmt_list); | |
a2086f98 | 2025 | /* Get a pointer to the revised statement. */ |
0666767e | 2026 | tree *revised = tsi_stmt_ptr (tsi_last (stmt_list)); |
49789fd0 | 2027 | if (needs_dtor) |
368ba7ae | 2028 | finish_expr_stmt (build_cleanup (var)); |
49789fd0 | 2029 | data->index += 2; |
0666767e | 2030 | |
368ba7ae IS |
2031 | /* Replace the original expression with the expansion. */ |
2032 | *expr = pop_stmt_list (stmt_list); | |
0666767e IS |
2033 | |
2034 | /* Now, if the awaitable had an initializer, expand any awaits that might | |
2035 | be embedded in it. */ | |
2036 | tree *aw_expr_ptr; | |
2037 | if (await_init && | |
2038 | cp_walk_tree (await_init, co_await_find_in_subtree, &aw_expr_ptr, NULL)) | |
2039 | expand_one_await_expression (await_init, aw_expr_ptr, d); | |
2040 | ||
027e3041 | 2041 | /* Expand any more await expressions in the original statement. */ |
0666767e IS |
2042 | if (cp_walk_tree (revised, co_await_find_in_subtree, &aw_expr_ptr, NULL)) |
2043 | expand_one_await_expression (revised, aw_expr_ptr, d); | |
2044 | ||
2045 | return NULL; | |
2046 | } | |
2047 | ||
2048 | /* Check to see if a statement contains at least one await expression, if | |
2049 | so, then process that. */ | |
2050 | ||
2051 | static tree | |
2052 | process_one_statement (tree *stmt, void *d) | |
2053 | { | |
2054 | tree *aw_expr_ptr; | |
2055 | if (cp_walk_tree (stmt, co_await_find_in_subtree, &aw_expr_ptr, NULL)) | |
2056 | expand_one_await_expression (stmt, aw_expr_ptr, d); | |
49789fd0 IS |
2057 | return NULL_TREE; |
2058 | } | |
2059 | ||
0666767e IS |
2060 | static tree |
2061 | await_statement_expander (tree *stmt, int *do_subtree, void *d) | |
2062 | { | |
2063 | tree res = NULL_TREE; | |
2064 | ||
2065 | /* Process a statement at a time. */ | |
3dbc7721 IS |
2066 | if (STATEMENT_CLASS_P (*stmt) || TREE_CODE (*stmt) == BIND_EXPR) |
2067 | return NULL_TREE; /* Just process the sub-trees. */ | |
0666767e IS |
2068 | else if (TREE_CODE (*stmt) == STATEMENT_LIST) |
2069 | { | |
0f54cc9c | 2070 | for (tree &s : tsi_range (*stmt)) |
0666767e | 2071 | { |
0f54cc9c | 2072 | res = cp_walk_tree (&s, await_statement_expander, |
0666767e IS |
2073 | d, NULL); |
2074 | if (res) | |
2075 | return res; | |
2076 | } | |
2077 | *do_subtree = 0; /* Done subtrees. */ | |
2078 | } | |
0666767e IS |
2079 | else if (EXPR_P (*stmt)) |
2080 | { | |
2081 | process_one_statement (stmt, d); | |
2082 | *do_subtree = 0; /* Done subtrees. */ | |
2083 | } | |
2084 | ||
2085 | /* Continue statement walk, where required. */ | |
2086 | return res; | |
2087 | } | |
2088 | ||
49789fd0 IS |
2089 | struct await_xform_data |
2090 | { | |
c3ccce5b | 2091 | tree actor_fn; /* Decl for context. */ |
49789fd0 | 2092 | tree actor_frame; |
ffd521d8 | 2093 | hash_map<tree, suspend_point_info> *suspend_points; |
49789fd0 IS |
2094 | }; |
2095 | ||
2096 | /* When we built the await expressions, we didn't know the coro frame | |
2097 | layout, therefore no idea where to find the promise or where to put | |
2098 | the awaitables. Now we know these things, fill them in. */ | |
2099 | ||
2100 | static tree | |
2101 | transform_await_expr (tree await_expr, await_xform_data *xform) | |
2102 | { | |
ffd521d8 | 2103 | suspend_point_info *si = xform->suspend_points->get (await_expr); |
49789fd0 IS |
2104 | location_t loc = EXPR_LOCATION (await_expr); |
2105 | if (!si) | |
2106 | { | |
2107 | error_at (loc, "no suspend point info for %qD", await_expr); | |
2108 | return error_mark_node; | |
2109 | } | |
2110 | ||
2111 | /* So, on entry, we have: | |
2112 | in : CO_AWAIT_EXPR (a, e_proxy, o, awr_call_vector, mode) | |
2113 | We no longer need a [it had diagnostic value, maybe?] | |
80222642 | 2114 | We need to replace the e_proxy in the awr_call. */ |
49789fd0 | 2115 | |
005530eb IS |
2116 | /* If we have a frame var for the awaitable, get a reference to it. */ |
2117 | proxy_replace data; | |
2118 | if (si->await_field_id) | |
2119 | { | |
049a927c IS |
2120 | tree as |
2121 | = coro_build_frame_access_expr (xform->actor_frame, si->await_field_id, | |
2122 | true, tf_warning_or_error); | |
005530eb IS |
2123 | /* Replace references to the instance proxy with the frame entry now |
2124 | computed. */ | |
2125 | data.from = TREE_OPERAND (await_expr, 1); | |
2126 | data.to = as; | |
2127 | cp_walk_tree (&await_expr, replace_proxy, &data, NULL); | |
2128 | ||
2129 | /* .. and replace. */ | |
2130 | TREE_OPERAND (await_expr, 1) = as; | |
2131 | } | |
49789fd0 | 2132 | |
49789fd0 IS |
2133 | return await_expr; |
2134 | } | |
2135 | ||
2136 | /* A wrapper for the transform_await_expr function so that it can be a | |
2137 | callback from cp_walk_tree. */ | |
2138 | ||
2139 | static tree | |
2140 | transform_await_wrapper (tree *stmt, int *do_subtree, void *d) | |
2141 | { | |
c3ccce5b J |
2142 | /* Set actor function as new DECL_CONTEXT of label_decl. */ |
2143 | struct await_xform_data *xform = (struct await_xform_data *) d; | |
2144 | if (TREE_CODE (*stmt) == LABEL_DECL | |
2145 | && DECL_CONTEXT (*stmt) != xform->actor_fn) | |
2146 | DECL_CONTEXT (*stmt) = xform->actor_fn; | |
2147 | ||
f1656ae9 IS |
2148 | /* We should have already lowered co_yields to their co_await. */ |
2149 | gcc_checking_assert (TREE_CODE (*stmt) != CO_YIELD_EXPR); | |
2150 | if (TREE_CODE (*stmt) != CO_AWAIT_EXPR) | |
49789fd0 IS |
2151 | return NULL_TREE; |
2152 | ||
2153 | tree await_expr = *stmt; | |
49789fd0 IS |
2154 | *stmt = transform_await_expr (await_expr, xform); |
2155 | if (*stmt == error_mark_node) | |
2156 | *do_subtree = 0; | |
2157 | return NULL_TREE; | |
2158 | } | |
2159 | ||
49789fd0 IS |
2160 | /* For figuring out what local variable usage we have. */ |
2161 | struct local_vars_transform | |
2162 | { | |
2163 | tree context; | |
2164 | tree actor_frame; | |
2165 | tree coro_frame_type; | |
2166 | location_t loc; | |
2167 | hash_map<tree, local_var_info> *local_var_uses; | |
2168 | }; | |
2169 | ||
2170 | static tree | |
2171 | transform_local_var_uses (tree *stmt, int *do_subtree, void *d) | |
2172 | { | |
2173 | local_vars_transform *lvd = (local_vars_transform *) d; | |
2174 | ||
2175 | /* For each var in this bind expr (that has a frame id, which means it was | |
88974974 | 2176 | accessed), build a frame reference and add it as the DECL_VALUE_EXPR. */ |
49789fd0 IS |
2177 | |
2178 | if (TREE_CODE (*stmt) == BIND_EXPR) | |
2179 | { | |
2180 | tree lvar; | |
2181 | for (lvar = BIND_EXPR_VARS (*stmt); lvar != NULL; | |
2182 | lvar = DECL_CHAIN (lvar)) | |
2183 | { | |
2184 | bool existed; | |
2185 | local_var_info &local_var | |
2186 | = lvd->local_var_uses->get_or_insert (lvar, &existed); | |
2187 | gcc_checking_assert (existed); | |
2188 | ||
2189 | /* Re-write the variable's context to be in the actor func. */ | |
2190 | DECL_CONTEXT (lvar) = lvd->context; | |
2191 | ||
88974974 IS |
2192 | /* For capture proxies, this could include the decl value expr. */ |
2193 | if (local_var.is_lambda_capture || local_var.has_value_expr_p) | |
cd14f288 | 2194 | continue; /* No frame entry for this. */ |
cd14f288 | 2195 | |
49789fd0 IS |
2196 | /* TODO: implement selective generation of fields when vars are |
2197 | known not-used. */ | |
2198 | if (local_var.field_id == NULL_TREE) | |
2199 | continue; /* Wasn't used. */ | |
049a927c IS |
2200 | tree fld_idx |
2201 | = coro_build_frame_access_expr (lvd->actor_frame, | |
2202 | local_var.field_id, true, | |
2203 | tf_warning_or_error); | |
49789fd0 | 2204 | local_var.field_idx = fld_idx; |
88974974 IS |
2205 | SET_DECL_VALUE_EXPR (lvar, fld_idx); |
2206 | DECL_HAS_VALUE_EXPR_P (lvar) = true; | |
448c89d5 | 2207 | } |
49789fd0 | 2208 | cp_walk_tree (&BIND_EXPR_BODY (*stmt), transform_local_var_uses, d, NULL); |
49789fd0 IS |
2209 | *do_subtree = 0; /* We've done the body already. */ |
2210 | return NULL_TREE; | |
2211 | } | |
49789fd0 IS |
2212 | return NULL_TREE; |
2213 | } | |
2214 | ||
fe550865 IS |
2215 | /* A helper to build the frame DTOR. |
2216 | [dcl.fct.def.coroutine] / 12 | |
2217 | The deallocation function’s name is looked up in the scope of the promise | |
2218 | type. If this lookup fails, the deallocation function’s name is looked up | |
2219 | in the global scope. If deallocation function lookup finds both a usual | |
2220 | deallocation function with only a pointer parameter and a usual | |
2221 | deallocation function with both a pointer parameter and a size parameter, | |
2222 | then the selected deallocation function shall be the one with two | |
2223 | parameters. Otherwise, the selected deallocation function shall be the | |
2224 | function with one parameter. If no usual deallocation function is found | |
2225 | the program is ill-formed. The selected deallocation function shall be | |
2226 | called with the address of the block of storage to be reclaimed as its | |
2227 | first argument. If a deallocation function with a parameter of type | |
2228 | std::size_t is used, the size of the block is passed as the corresponding | |
2229 | argument. */ | |
2230 | ||
2231 | static tree | |
624fb5b4 | 2232 | build_coroutine_frame_delete_expr (tree, tree, tree, location_t); |
fe550865 | 2233 | |
49789fd0 IS |
2234 | /* The actor transform. */ |
2235 | ||
2236 | static void | |
2237 | build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, | |
c5a735fa | 2238 | tree orig, hash_map<tree, local_var_info> *local_var_uses, |
ffd521d8 | 2239 | hash_map<tree, suspend_point_info> *suspend_points, |
d5da5f00 | 2240 | vec<tree> *param_dtor_list, |
6303cd7e IS |
2241 | tree resume_idx_var, unsigned body_count, tree frame_size, |
2242 | bool inline_p) | |
49789fd0 IS |
2243 | { |
2244 | verify_stmt_tree (fnbody); | |
2245 | /* Some things we inherit from the original function. */ | |
49789fd0 IS |
2246 | tree promise_type = get_coroutine_promise_type (orig); |
2247 | tree promise_proxy = get_coroutine_promise_proxy (orig); | |
49789fd0 IS |
2248 | |
2249 | /* One param, the coro frame pointer. */ | |
3904cc10 | 2250 | tree actor_fp = DECL_ARGUMENTS (actor); |
49789fd0 | 2251 | |
6303cd7e IS |
2252 | bool spf = start_preparsed_function (actor, NULL_TREE, SF_PRE_PARSED); |
2253 | gcc_checking_assert (spf); | |
2254 | gcc_checking_assert (cfun && current_function_decl && TREE_STATIC (actor)); | |
2255 | tree stmt = begin_function_body (); | |
49789fd0 | 2256 | |
49789fd0 IS |
2257 | tree actor_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); |
2258 | tree top_block = make_node (BLOCK); | |
2259 | BIND_EXPR_BLOCK (actor_bind) = top_block; | |
2260 | ||
c5a735fa | 2261 | tree continuation = coro_build_artificial_var (loc, coro_actor_continue_id, |
a45a7ecd IS |
2262 | void_coro_handle_type, actor, |
2263 | NULL_TREE); | |
2264 | ||
6d85947d | 2265 | BIND_EXPR_VARS (actor_bind) = continuation; |
c5a735fa | 2266 | BLOCK_VARS (top_block) = BIND_EXPR_VARS (actor_bind) ; |
6d85947d | 2267 | |
9252a208 IS |
2268 | /* Link in the block associated with the outer scope of the re-written |
2269 | function body. */ | |
49789fd0 | 2270 | tree first = expr_first (fnbody); |
9252a208 IS |
2271 | gcc_checking_assert (first && TREE_CODE (first) == BIND_EXPR); |
2272 | tree block = BIND_EXPR_BLOCK (first); | |
2273 | gcc_checking_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE); | |
2274 | gcc_checking_assert (BLOCK_CHAIN (block) == NULL_TREE); | |
2275 | BLOCK_SUPERCONTEXT (block) = top_block; | |
2276 | BLOCK_SUBBLOCKS (top_block) = block; | |
49789fd0 IS |
2277 | |
2278 | add_stmt (actor_bind); | |
2279 | tree actor_body = push_stmt_list (); | |
2280 | ||
2281 | /* The entry point for the actor code from the ramp. */ | |
2282 | tree actor_begin_label | |
2283 | = create_named_label_with_ctx (loc, "actor.begin", actor); | |
2284 | tree actor_frame = build1_loc (loc, INDIRECT_REF, coro_frame_type, actor_fp); | |
2285 | ||
6d85947d IS |
2286 | /* Declare the continuation handle. */ |
2287 | add_decl_expr (continuation); | |
2288 | ||
49789fd0 IS |
2289 | /* Re-write local vars, similarly. */ |
2290 | local_vars_transform xform_vars_data | |
2291 | = {actor, actor_frame, coro_frame_type, loc, local_var_uses}; | |
2292 | cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL); | |
049a927c IS |
2293 | tree rat = coro_build_frame_access_expr (actor_frame, coro_resume_index_id, |
2294 | false, tf_warning_or_error); | |
49789fd0 IS |
2295 | tree ret_label |
2296 | = create_named_label_with_ctx (loc, "actor.suspend.ret", actor); | |
2297 | ||
6d85947d IS |
2298 | tree continue_label |
2299 | = create_named_label_with_ctx (loc, "actor.continue.ret", actor); | |
2300 | ||
368ba7ae IS |
2301 | /* Build the dispatcher; for each await expression there is an odd entry |
2302 | corresponding to the destruction action and an even entry for the resume | |
2303 | one: | |
2304 | if (resume index is odd) | |
2305 | { | |
2306 | switch (resume index) | |
2307 | case 1: | |
2308 | goto cleanup. | |
2309 | case ... odd suspension point number | |
2310 | .CO_ACTOR (... odd suspension point number) | |
2311 | break; | |
2312 | default: | |
2313 | break; | |
2314 | } | |
2315 | else | |
2316 | { | |
2317 | coro.restart.dispatch: | |
2318 | case 0: | |
2319 | goto start. | |
2320 | case ... even suspension point number | |
2321 | .CO_ACTOR (... even suspension point number) | |
2322 | break; | |
2323 | default: | |
2324 | break; | |
2325 | } | |
2326 | we should not get here unless something is broken badly. | |
2327 | __builtin_trap (); | |
2328 | */ | |
49789fd0 IS |
2329 | tree lsb_if = begin_if_stmt (); |
2330 | tree chkb0 = build2 (BIT_AND_EXPR, short_unsigned_type_node, rat, | |
2331 | build_int_cst (short_unsigned_type_node, 1)); | |
2332 | chkb0 = build2 (NE_EXPR, short_unsigned_type_node, chkb0, | |
2333 | build_int_cst (short_unsigned_type_node, 0)); | |
2334 | finish_if_stmt_cond (chkb0, lsb_if); | |
2335 | ||
2336 | tree destroy_dispatcher = begin_switch_stmt (); | |
2337 | finish_switch_cond (rat, destroy_dispatcher); | |
49789fd0 | 2338 | |
020b286c IS |
2339 | /* The destroy point numbered #1 is special, in that it is reached from a |
2340 | coroutine that is suspended after re-throwing from unhandled_exception(). | |
2341 | This label just invokes the cleanup of promise, param copies and the | |
2342 | frame itself. */ | |
2343 | tree del_promise_label | |
2344 | = create_named_label_with_ctx (loc, "coro.delete.promise", actor); | |
ff0cba20 AA |
2345 | finish_case_label (loc, build_int_cst (short_unsigned_type_node, 1), |
2346 | NULL_TREE); | |
020b286c IS |
2347 | add_stmt (build_stmt (loc, GOTO_EXPR, del_promise_label)); |
2348 | ||
49789fd0 | 2349 | short unsigned lab_num = 3; |
9252a208 | 2350 | for (unsigned destr_pt = 0; destr_pt < body_count; destr_pt++) |
49789fd0 IS |
2351 | { |
2352 | tree l_num = build_int_cst (short_unsigned_type_node, lab_num); | |
ff0cba20 | 2353 | finish_case_label (loc, l_num, NULL_TREE); |
368ba7ae IS |
2354 | tree c = build_call_expr_internal_loc (loc, IFN_CO_ACTOR, void_type_node, |
2355 | 1, l_num); | |
2356 | finish_expr_stmt (c); | |
2357 | finish_break_stmt (); | |
49789fd0 IS |
2358 | lab_num += 2; |
2359 | } | |
368ba7ae IS |
2360 | finish_case_label (loc, NULL_TREE, NULL_TREE); |
2361 | finish_break_stmt (); | |
49789fd0 | 2362 | |
368ba7ae | 2363 | /* Finish the destroy dispatcher. */ |
49789fd0 IS |
2364 | finish_switch_stmt (destroy_dispatcher); |
2365 | ||
2366 | finish_then_clause (lsb_if); | |
21b4d0ef | 2367 | begin_else_clause (lsb_if); |
49789fd0 | 2368 | |
368ba7ae IS |
2369 | /* For the case of a boolean await_resume () that returns 'true' we should |
2370 | restart the dispatch, since we cannot know if additional resumes were | |
2371 | executed from within the await_suspend function. */ | |
2372 | tree restart_dispatch_label | |
2373 | = create_named_label_with_ctx (loc, "coro.restart.dispatch", actor); | |
2374 | add_stmt (build_stmt (loc, LABEL_EXPR, restart_dispatch_label)); | |
2375 | ||
49789fd0 IS |
2376 | tree dispatcher = begin_switch_stmt (); |
2377 | finish_switch_cond (rat, dispatcher); | |
ff0cba20 AA |
2378 | finish_case_label (loc, build_int_cst (short_unsigned_type_node, 0), |
2379 | NULL_TREE); | |
368ba7ae | 2380 | add_stmt (build_stmt (loc, GOTO_EXPR, actor_begin_label)); |
49789fd0 IS |
2381 | |
2382 | lab_num = 2; | |
9252a208 IS |
2383 | /* The final resume should be made to hit the default (trap, UB) entry |
2384 | although it will be unreachable via the normal entry point, since that | |
2385 | is set to NULL on reaching final suspend. */ | |
2386 | for (unsigned resu_pt = 0; resu_pt < body_count; resu_pt++) | |
49789fd0 IS |
2387 | { |
2388 | tree l_num = build_int_cst (short_unsigned_type_node, lab_num); | |
ff0cba20 | 2389 | finish_case_label (loc, l_num, NULL_TREE); |
368ba7ae IS |
2390 | tree c = build_call_expr_internal_loc (loc, IFN_CO_ACTOR, void_type_node, |
2391 | 1, l_num); | |
2392 | finish_expr_stmt (c); | |
2393 | finish_break_stmt (); | |
49789fd0 IS |
2394 | lab_num += 2; |
2395 | } | |
368ba7ae IS |
2396 | finish_case_label (loc, NULL_TREE, NULL_TREE); |
2397 | finish_break_stmt (); | |
49789fd0 | 2398 | |
368ba7ae | 2399 | /* Finish the resume dispatcher. */ |
49789fd0 | 2400 | finish_switch_stmt (dispatcher); |
21b4d0ef | 2401 | finish_else_clause (lsb_if); |
49789fd0 IS |
2402 | |
2403 | finish_if_stmt (lsb_if); | |
2404 | ||
368ba7ae IS |
2405 | /* If we reach here then we've hit UB. */ |
2406 | tree t = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); | |
2407 | finish_expr_stmt (t); | |
2408 | ||
2409 | /* Now we start building the rewritten function body. */ | |
2410 | add_stmt (build_stmt (loc, LABEL_EXPR, actor_begin_label)); | |
49789fd0 | 2411 | |
49789fd0 | 2412 | /* actor's coroutine 'self handle'. */ |
049a927c IS |
2413 | tree ash = coro_build_frame_access_expr (actor_frame, coro_self_handle_id, |
2414 | false, tf_warning_or_error); | |
49789fd0 | 2415 | /* So construct the self-handle from the frame address. */ |
5b4476a1 AA |
2416 | tree hfa_m = get_coroutine_from_address (orig); |
2417 | /* Should have been set earlier by coro_promise_type_found_p. */ | |
2418 | gcc_assert (hfa_m); | |
49789fd0 | 2419 | |
368ba7ae | 2420 | tree r = build1 (CONVERT_EXPR, build_pointer_type (void_type_node), actor_fp); |
49789fd0 IS |
2421 | vec<tree, va_gc> *args = make_tree_vector_single (r); |
2422 | tree hfa = build_new_method_call (ash, hfa_m, &args, NULL_TREE, LOOKUP_NORMAL, | |
2423 | NULL, tf_warning_or_error); | |
6ffbf87c | 2424 | r = cp_build_init_expr (ash, hfa); |
49789fd0 IS |
2425 | r = coro_build_cvt_void_expr_stmt (r, loc); |
2426 | add_stmt (r); | |
2427 | release_tree_vector (args); | |
2428 | ||
2429 | /* Now we know the real promise, and enough about the frame layout to | |
2430 | decide where to put things. */ | |
2431 | ||
ffd521d8 | 2432 | await_xform_data xform = {actor, actor_frame, suspend_points}; |
49789fd0 | 2433 | |
49789fd0 IS |
2434 | /* Transform the await expressions in the function body. Only do each |
2435 | await tree once! */ | |
2436 | hash_set<tree> pset; | |
2437 | cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset); | |
2438 | ||
9252a208 IS |
2439 | /* Add in our function body with the co_returns rewritten to final form. */ |
2440 | add_stmt (fnbody); | |
49789fd0 IS |
2441 | |
2442 | /* now do the tail of the function. */ | |
49789fd0 IS |
2443 | r = build_stmt (loc, LABEL_EXPR, del_promise_label); |
2444 | add_stmt (r); | |
2445 | ||
2446 | /* Destructors for the things we built explicitly. */ | |
44868e72 JM |
2447 | if (tree c = cxx_maybe_build_cleanup (promise_proxy, tf_warning_or_error)) |
2448 | add_stmt (c); | |
49789fd0 IS |
2449 | |
2450 | tree del_frame_label | |
2451 | = create_named_label_with_ctx (loc, "coro.delete.frame", actor); | |
2452 | r = build_stmt (loc, LABEL_EXPR, del_frame_label); | |
2453 | add_stmt (r); | |
2454 | ||
2455 | /* Here deallocate the frame (if we allocated it), which we will have at | |
2456 | present. */ | |
049a927c IS |
2457 | tree fnf2_x |
2458 | = coro_build_frame_access_expr (actor_frame, coro_frame_needs_free_id, | |
2459 | false, tf_warning_or_error); | |
49789fd0 IS |
2460 | |
2461 | tree need_free_if = begin_if_stmt (); | |
2462 | fnf2_x = build1 (CONVERT_EXPR, integer_type_node, fnf2_x); | |
2463 | tree cmp = build2 (NE_EXPR, integer_type_node, fnf2_x, integer_zero_node); | |
2464 | finish_if_stmt_cond (cmp, need_free_if); | |
d5da5f00 | 2465 | while (!param_dtor_list->is_empty ()) |
49789fd0 | 2466 | { |
049a927c IS |
2467 | tree parm_id = param_dtor_list->pop (); |
2468 | tree a = coro_build_frame_access_expr (actor_frame, parm_id, false, | |
2469 | tf_warning_or_error); | |
d5da5f00 IS |
2470 | if (tree dtor = cxx_maybe_build_cleanup (a, tf_warning_or_error)) |
2471 | add_stmt (dtor); | |
49789fd0 IS |
2472 | } |
2473 | ||
fe550865 | 2474 | /* Build the frame DTOR. */ |
624fb5b4 IS |
2475 | tree del_coro_fr |
2476 | = build_coroutine_frame_delete_expr (actor_fp, frame_size, | |
2477 | promise_type, loc); | |
fe550865 | 2478 | finish_expr_stmt (del_coro_fr); |
49789fd0 IS |
2479 | finish_then_clause (need_free_if); |
2480 | tree scope = IF_SCOPE (need_free_if); | |
2481 | IF_SCOPE (need_free_if) = NULL; | |
2482 | r = do_poplevel (scope); | |
2483 | add_stmt (r); | |
2484 | ||
2485 | /* done. */ | |
2486 | r = build_stmt (loc, RETURN_EXPR, NULL); | |
65870e75 | 2487 | suppress_warning (r); /* We don't want a warning about this. */ |
49789fd0 IS |
2488 | r = maybe_cleanup_point_expr_void (r); |
2489 | add_stmt (r); | |
2490 | ||
2491 | /* This is the suspend return point. */ | |
2492 | r = build_stmt (loc, LABEL_EXPR, ret_label); | |
2493 | add_stmt (r); | |
2494 | ||
2495 | r = build_stmt (loc, RETURN_EXPR, NULL); | |
65870e75 | 2496 | suppress_warning (r); /* We don't want a warning about this. */ |
49789fd0 IS |
2497 | r = maybe_cleanup_point_expr_void (r); |
2498 | add_stmt (r); | |
2499 | ||
6d85947d IS |
2500 | /* This is the 'continuation' return point. For such a case we have a coro |
2501 | handle (from the await_suspend() call) and we want handle.resume() to | |
2502 | execute as a tailcall allowing arbitrary chaining of coroutines. */ | |
2503 | r = build_stmt (loc, LABEL_EXPR, continue_label); | |
2504 | add_stmt (r); | |
2505 | ||
5b4476a1 AA |
2506 | /* Should have been set earlier by the coro_initialized code. */ |
2507 | gcc_assert (void_coro_handle_address); | |
2508 | ||
6d85947d IS |
2509 | /* We want to force a tail-call even for O0/1, so this expands the resume |
2510 | call into its underlying implementation. */ | |
5b4476a1 AA |
2511 | tree addr = build_new_method_call (continuation, void_coro_handle_address, |
2512 | NULL, NULL_TREE, LOOKUP_NORMAL, NULL, | |
2513 | tf_warning_or_error); | |
6d85947d IS |
2514 | tree resume = build_call_expr_loc |
2515 | (loc, builtin_decl_explicit (BUILT_IN_CORO_RESUME), 1, addr); | |
2516 | ||
a126a157 | 2517 | /* In order to support an arbitrary number of coroutine continuations, |
d0ce5bae IS |
2518 | we must tail call them. However, some targets do not support indirect |
2519 | tail calls to arbitrary callees. See PR94359. */ | |
6d85947d | 2520 | CALL_EXPR_TAILCALL (resume) = true; |
6d85947d IS |
2521 | resume = coro_build_cvt_void_expr_stmt (resume, loc); |
2522 | add_stmt (resume); | |
2523 | ||
2524 | r = build_stmt (loc, RETURN_EXPR, NULL); | |
2525 | gcc_checking_assert (maybe_cleanup_point_expr_void (r) == r); | |
2526 | add_stmt (r); | |
2527 | ||
49789fd0 IS |
2528 | /* We've now rewritten the tree and added the initial and final |
2529 | co_awaits. Now pass over the tree and expand the co_awaits. */ | |
6d85947d | 2530 | |
c5a735fa | 2531 | coro_aw_data data = {actor, actor_fp, resume_idx_var, NULL_TREE, |
6d85947d | 2532 | ash, del_promise_label, ret_label, |
368ba7ae | 2533 | continue_label, restart_dispatch_label, continuation, 2}; |
0666767e | 2534 | cp_walk_tree (&actor_body, await_statement_expander, &data, NULL); |
49789fd0 | 2535 | |
234681ea IS |
2536 | BIND_EXPR_BODY (actor_bind) = pop_stmt_list (actor_body); |
2537 | TREE_SIDE_EFFECTS (actor_bind) = true; | |
49789fd0 | 2538 | |
6303cd7e IS |
2539 | cfun->coroutine_component = 1; |
2540 | finish_function_body (stmt); | |
2541 | finish_function (inline_p); | |
49789fd0 IS |
2542 | } |
2543 | ||
2544 | /* The prototype 'destroy' function : | |
c5a735fa | 2545 | frame->__Coro_resume_index |= 1; |
49789fd0 IS |
2546 | actor (frame); */ |
2547 | ||
2548 | static void | |
2549 | build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy, | |
6303cd7e | 2550 | tree actor, bool inline_p) |
49789fd0 IS |
2551 | { |
2552 | /* One param, the coro frame pointer. */ | |
3904cc10 | 2553 | tree destr_fp = DECL_ARGUMENTS (destroy); |
6303cd7e IS |
2554 | gcc_checking_assert (POINTER_TYPE_P (TREE_TYPE (destr_fp)) |
2555 | && same_type_p (coro_frame_type, | |
2556 | TREE_TYPE (TREE_TYPE (destr_fp)))); | |
49789fd0 | 2557 | |
6303cd7e IS |
2558 | bool spf = start_preparsed_function (destroy, NULL_TREE, SF_PRE_PARSED); |
2559 | gcc_checking_assert (spf); | |
2560 | tree dstr_stmt = begin_function_body (); | |
49789fd0 | 2561 | |
6303cd7e IS |
2562 | tree destr_frame |
2563 | = cp_build_indirect_ref (loc, destr_fp, RO_UNARY_STAR, | |
2564 | tf_warning_or_error); | |
49789fd0 | 2565 | |
049a927c IS |
2566 | tree rat = coro_build_frame_access_expr (destr_frame, coro_resume_index_id, |
2567 | false, tf_warning_or_error); | |
49789fd0 IS |
2568 | |
2569 | /* _resume_at |= 1 */ | |
6303cd7e IS |
2570 | tree dstr_idx |
2571 | = build2_loc (loc, BIT_IOR_EXPR, short_unsigned_type_node, rat, | |
2572 | build_int_cst (short_unsigned_type_node, 1)); | |
2573 | tree r = cp_build_modify_expr (loc, rat, NOP_EXPR, dstr_idx, | |
2574 | tf_warning_or_error); | |
2575 | finish_expr_stmt (r); | |
49789fd0 IS |
2576 | |
2577 | /* So .. call the actor .. */ | |
6303cd7e | 2578 | finish_expr_stmt (build_call_expr_loc (loc, actor, 1, destr_fp)); |
49789fd0 IS |
2579 | |
2580 | /* done. */ | |
6303cd7e | 2581 | finish_return_stmt (NULL_TREE); |
49789fd0 | 2582 | |
6303cd7e IS |
2583 | gcc_checking_assert (cfun && current_function_decl); |
2584 | cfun->coroutine_component = 1; | |
2585 | finish_function_body (dstr_stmt); | |
2586 | finish_function (inline_p); | |
49789fd0 IS |
2587 | } |
2588 | ||
2589 | /* Helper that returns an identifier for an appended extension to the | |
2590 | current un-mangled function name. */ | |
2591 | ||
2592 | static tree | |
2593 | get_fn_local_identifier (tree orig, const char *append) | |
2594 | { | |
2595 | /* Figure out the bits we need to generate names for the outlined things | |
2596 | For consistency, this needs to behave the same way as | |
2597 | ASM_FORMAT_PRIVATE_NAME does. */ | |
2598 | tree nm = DECL_NAME (orig); | |
2599 | const char *sep, *pfx = ""; | |
2600 | #ifndef NO_DOT_IN_LABEL | |
2601 | sep = "."; | |
2602 | #else | |
2603 | #ifndef NO_DOLLAR_IN_LABEL | |
dba33cb7 | 2604 | sep = "$"; |
49789fd0 IS |
2605 | #else |
2606 | sep = "_"; | |
2607 | pfx = "__"; | |
2608 | #endif | |
2609 | #endif | |
2610 | ||
2611 | char *an; | |
2612 | if (DECL_ASSEMBLER_NAME (orig)) | |
2613 | an = ACONCAT ((IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (orig)), sep, append, | |
2614 | (char *) 0)); | |
2615 | else if (DECL_USE_TEMPLATE (orig) && DECL_TEMPLATE_INFO (orig) | |
2616 | && DECL_TI_ARGS (orig)) | |
2617 | { | |
2618 | tree tpl_args = DECL_TI_ARGS (orig); | |
2619 | an = ACONCAT ((pfx, IDENTIFIER_POINTER (nm), (char *) 0)); | |
2620 | for (int i = 0; i < TREE_VEC_LENGTH (tpl_args); ++i) | |
2621 | { | |
2622 | tree typ = DECL_NAME (TYPE_NAME (TREE_VEC_ELT (tpl_args, i))); | |
2623 | an = ACONCAT ((an, sep, IDENTIFIER_POINTER (typ), (char *) 0)); | |
2624 | } | |
2625 | an = ACONCAT ((an, sep, append, (char *) 0)); | |
2626 | } | |
2627 | else | |
2628 | an = ACONCAT ((pfx, IDENTIFIER_POINTER (nm), sep, append, (char *) 0)); | |
2629 | ||
2630 | return get_identifier (an); | |
2631 | } | |
2632 | ||
e74c7607 IS |
2633 | /* Build an initial or final await initialized from the promise |
2634 | initial_suspend or final_suspend expression. */ | |
2635 | ||
49789fd0 IS |
2636 | static tree |
2637 | build_init_or_final_await (location_t loc, bool is_final) | |
2638 | { | |
2639 | tree suspend_alt = is_final ? coro_final_suspend_identifier | |
2640 | : coro_initial_suspend_identifier; | |
49789fd0 | 2641 | |
e74c7607 IS |
2642 | tree setup_call |
2643 | = coro_build_promise_expression (current_function_decl, NULL, suspend_alt, | |
2644 | loc, NULL, /*musthave=*/true); | |
49789fd0 | 2645 | |
9ee91079 IS |
2646 | /* Check for noexcept on the final_suspend call. */ |
2647 | if (flag_exceptions && is_final && setup_call != error_mark_node | |
2648 | && coro_diagnose_throwing_final_aw_expr (setup_call)) | |
2649 | return error_mark_node; | |
2650 | ||
49789fd0 | 2651 | /* So build the co_await for this */ |
d5029d45 | 2652 | /* For initial/final suspends the call is "a" per [expr.await] 3.2. */ |
49789fd0 IS |
2653 | return build_co_await (loc, setup_call, (is_final ? FINAL_SUSPEND_POINT |
2654 | : INITIAL_SUSPEND_POINT)); | |
2655 | } | |
2656 | ||
2657 | /* Callback to record the essential data for each await point found in the | |
2658 | function. */ | |
2659 | ||
2660 | static bool | |
ffd521d8 IS |
2661 | register_await_info (tree await_expr, tree aw_type, tree aw_nam, |
2662 | hash_map<tree, suspend_point_info> *suspend_points) | |
49789fd0 IS |
2663 | { |
2664 | bool seen; | |
2665 | suspend_point_info &s | |
2666 | = suspend_points->get_or_insert (await_expr, &seen); | |
2667 | if (seen) | |
2668 | { | |
0f66b848 | 2669 | warning_at (EXPR_LOCATION (await_expr), 0, "duplicate info for %qE", |
49789fd0 IS |
2670 | await_expr); |
2671 | return false; | |
2672 | } | |
2673 | s.awaitable_type = aw_type; | |
2674 | s.await_field_id = aw_nam; | |
49789fd0 IS |
2675 | return true; |
2676 | } | |
2677 | ||
0f66b848 IS |
2678 | /* If this is an await expression, then count it (both uniquely within the |
2679 | function and locally within a single statement). */ | |
49789fd0 IS |
2680 | |
2681 | static tree | |
0f66b848 | 2682 | register_awaits (tree *stmt, int *, void *d) |
49789fd0 | 2683 | { |
0f66b848 IS |
2684 | tree aw_expr = *stmt; |
2685 | ||
f1656ae9 | 2686 | /* We should have already lowered co_yields to their co_await. */ |
0f66b848 IS |
2687 | gcc_checking_assert (TREE_CODE (aw_expr) != CO_YIELD_EXPR); |
2688 | ||
2689 | if (TREE_CODE (aw_expr) != CO_AWAIT_EXPR) | |
2690 | return NULL_TREE; | |
f1656ae9 | 2691 | |
0f66b848 IS |
2692 | /* Count how many awaits the current expression contains. */ |
2693 | susp_frame_data *data = (susp_frame_data *) d; | |
2694 | data->saw_awaits++; | |
2695 | /* Each await suspend context is unique, this is a function-wide value. */ | |
2696 | data->await_number++; | |
2697 | ||
2698 | /* Awaitables should either be user-locals or promoted to coroutine frame | |
2699 | entries at this point, and their initializers should have been broken | |
2700 | out. */ | |
2701 | tree aw = TREE_OPERAND (aw_expr, 1); | |
2702 | gcc_checking_assert (!TREE_OPERAND (aw_expr, 2)); | |
2703 | ||
2704 | tree aw_field_type = TREE_TYPE (aw); | |
2705 | tree aw_field_nam = NULL_TREE; | |
ffd521d8 | 2706 | register_await_info (aw_expr, aw_field_type, aw_field_nam, data->suspend_points); |
0f66b848 IS |
2707 | |
2708 | /* Rewrite target expressions on the await_suspend () to remove extraneous | |
2709 | cleanups for the awaitables, which are now promoted to frame vars and | |
2710 | managed via that. */ | |
2711 | tree v = TREE_OPERAND (aw_expr, 3); | |
2712 | tree o = TREE_VEC_ELT (v, 1); | |
2713 | if (TREE_CODE (o) == TARGET_EXPR) | |
1fea6f82 | 2714 | TREE_VEC_ELT (v, 1) = get_target_expr (TARGET_EXPR_INITIAL (o)); |
0f66b848 IS |
2715 | return NULL_TREE; |
2716 | } | |
2717 | ||
2718 | /* There are cases where any await expression is relevant. */ | |
2719 | static tree | |
2720 | find_any_await (tree *stmt, int *dosub, void *d) | |
2721 | { | |
f1656ae9 | 2722 | if (TREE_CODE (*stmt) == CO_AWAIT_EXPR) |
49789fd0 | 2723 | { |
0f66b848 IS |
2724 | *dosub = 0; /* We don't need to consider this any further. */ |
2725 | tree **p = (tree **) d; | |
2726 | *p = stmt; | |
2727 | return *stmt; | |
49789fd0 | 2728 | } |
0f66b848 IS |
2729 | return NULL_TREE; |
2730 | } | |
49789fd0 | 2731 | |
0f66b848 IS |
2732 | static bool |
2733 | tmp_target_expr_p (tree t) | |
2734 | { | |
2735 | if (TREE_CODE (t) != TARGET_EXPR) | |
2736 | return false; | |
1fea6f82 | 2737 | tree v = TARGET_EXPR_SLOT (t); |
0f66b848 IS |
2738 | if (!DECL_ARTIFICIAL (v)) |
2739 | return false; | |
2740 | if (DECL_NAME (v)) | |
2741 | return false; | |
2742 | return true; | |
2743 | } | |
2744 | ||
2745 | /* Structure to record sub-expressions that need to be handled by the | |
2746 | statement flattener. */ | |
49789fd0 | 2747 | |
0f66b848 IS |
2748 | struct coro_interesting_subtree |
2749 | { | |
2750 | tree* entry; | |
2751 | hash_set<tree> *temps_used; | |
2752 | }; | |
2753 | ||
2754 | /* tree-walk callback that returns the first encountered sub-expression of | |
2755 | a kind that needs to be handled specifically by the statement flattener. */ | |
2756 | ||
2757 | static tree | |
2758 | find_interesting_subtree (tree *expr_p, int *dosub, void *d) | |
2759 | { | |
2760 | tree expr = *expr_p; | |
2761 | coro_interesting_subtree *p = (coro_interesting_subtree *)d; | |
2762 | if (TREE_CODE (expr) == CO_AWAIT_EXPR) | |
49789fd0 | 2763 | { |
0f66b848 IS |
2764 | *dosub = 0; /* We don't need to consider this any further. */ |
2765 | if (TREE_OPERAND (expr, 2)) | |
49789fd0 | 2766 | { |
0f66b848 IS |
2767 | p->entry = expr_p; |
2768 | return expr; | |
49789fd0 | 2769 | } |
0f66b848 IS |
2770 | } |
2771 | else if (tmp_target_expr_p (expr) | |
58a7b1e3 | 2772 | && !TARGET_EXPR_ELIDING_P (expr) |
0f66b848 IS |
2773 | && !p->temps_used->contains (expr)) |
2774 | { | |
2775 | p->entry = expr_p; | |
2776 | return expr; | |
2777 | } | |
49789fd0 | 2778 | |
0f66b848 IS |
2779 | return NULL_TREE; |
2780 | } | |
49789fd0 | 2781 | |
0f66b848 IS |
2782 | /* Node for a doubly-linked list of promoted variables and their |
2783 | initializers. When the initializer is a conditional expression | |
2784 | the 'then' and 'else' clauses are represented by a linked list | |
2785 | attached to then_cl and else_cl respectively. */ | |
49789fd0 | 2786 | |
0f66b848 IS |
2787 | struct var_nest_node |
2788 | { | |
2789 | var_nest_node () = default; | |
2790 | var_nest_node (tree v, tree i, var_nest_node *p, var_nest_node *n) | |
3bcf1921 | 2791 | : var(v), init(i), prev(p), next(n), then_cl (NULL), else_cl (NULL) |
0f66b848 IS |
2792 | { |
2793 | if (p) | |
2794 | p->next = this; | |
2795 | if (n) | |
2796 | n->prev = this; | |
2797 | } | |
2798 | tree var; | |
2799 | tree init; | |
2800 | var_nest_node *prev; | |
2801 | var_nest_node *next; | |
2802 | var_nest_node *then_cl; | |
2803 | var_nest_node *else_cl; | |
2804 | }; | |
49789fd0 | 2805 | |
0f66b848 IS |
2806 | /* This is called for single statements from the co-await statement walker. |
2807 | It checks to see if the statement contains any initializers for awaitables | |
2808 | and if any of these capture items by reference. */ | |
49789fd0 | 2809 | |
0f66b848 IS |
2810 | static void |
2811 | flatten_await_stmt (var_nest_node *n, hash_set<tree> *promoted, | |
2812 | hash_set<tree> *temps_used, tree *replace_in) | |
2813 | { | |
2814 | bool init_expr = false; | |
2815 | switch (TREE_CODE (n->init)) | |
2816 | { | |
2817 | default: break; | |
2818 | /* Compound expressions must be flattened specifically. */ | |
2819 | case COMPOUND_EXPR: | |
7f327e87 | 2820 | { |
0f66b848 IS |
2821 | tree first = TREE_OPERAND (n->init, 0); |
2822 | n->init = TREE_OPERAND (n->init, 1); | |
2823 | var_nest_node *ins | |
2824 | = new var_nest_node(NULL_TREE, first, n->prev, n); | |
2825 | /* The compiler (but not the user) can generate temporaries with | |
2826 | uses in the second arm of a compound expr. */ | |
2827 | flatten_await_stmt (ins, promoted, temps_used, &n->init); | |
2828 | flatten_await_stmt (n, promoted, temps_used, NULL); | |
2829 | /* The two arms have been processed separately. */ | |
2830 | return; | |
7f327e87 | 2831 | } |
0f66b848 IS |
2832 | break; |
2833 | /* Handle conditional expressions. */ | |
2834 | case INIT_EXPR: | |
2835 | init_expr = true; | |
2836 | /* FALLTHROUGH */ | |
2837 | case MODIFY_EXPR: | |
2838 | { | |
2839 | tree old_expr = TREE_OPERAND (n->init, 1); | |
2840 | if (TREE_CODE (old_expr) == COMPOUND_EXPR) | |
2841 | { | |
2842 | tree first = TREE_OPERAND (old_expr, 0); | |
2843 | TREE_OPERAND (n->init, 1) = TREE_OPERAND (old_expr, 1); | |
2844 | var_nest_node *ins | |
2845 | = new var_nest_node(NULL_TREE, first, n->prev, n); | |
2846 | flatten_await_stmt (ins, promoted, temps_used, | |
2847 | &TREE_OPERAND (n->init, 1)); | |
2848 | flatten_await_stmt (n, promoted, temps_used, NULL); | |
2849 | return; | |
2850 | } | |
2851 | if (TREE_CODE (old_expr) != COND_EXPR) | |
2852 | break; | |
2853 | /* Reconstruct x = t ? y : z; | |
2854 | as (void) t ? x = y : x = z; */ | |
2855 | tree var = TREE_OPERAND (n->init, 0); | |
2856 | tree var_type = TREE_TYPE (var); | |
2857 | tree cond = COND_EXPR_COND (old_expr); | |
2858 | /* We are allowed a void type throw in one or both of the cond | |
2859 | expr arms. */ | |
2860 | tree then_cl = COND_EXPR_THEN (old_expr); | |
2861 | if (!VOID_TYPE_P (TREE_TYPE (then_cl))) | |
2862 | { | |
2863 | gcc_checking_assert (TREE_CODE (then_cl) != STATEMENT_LIST); | |
6ffbf87c JM |
2864 | if (init_expr) |
2865 | then_cl = cp_build_init_expr (var, then_cl); | |
2866 | else | |
2867 | then_cl = build2 (MODIFY_EXPR, var_type, var, then_cl); | |
0f66b848 IS |
2868 | } |
2869 | tree else_cl = COND_EXPR_ELSE (old_expr); | |
2870 | if (!VOID_TYPE_P (TREE_TYPE (else_cl))) | |
2871 | { | |
9c6344c1 | 2872 | gcc_checking_assert (TREE_CODE (else_cl) != STATEMENT_LIST); |
6ffbf87c JM |
2873 | if (init_expr) |
2874 | else_cl = cp_build_init_expr (var, else_cl); | |
2875 | else | |
2876 | else_cl = build2 (MODIFY_EXPR, var_type, var, else_cl); | |
0f66b848 IS |
2877 | } |
2878 | n->init = build3 (COND_EXPR, var_type, cond, then_cl, else_cl); | |
2879 | } | |
2880 | /* FALLTHROUGH */ | |
2881 | case COND_EXPR: | |
2882 | { | |
2883 | tree *found; | |
2884 | tree cond = COND_EXPR_COND (n->init); | |
2885 | /* If the condition contains an await expression, then we need to | |
2886 | set that first and use a separate var. */ | |
2887 | if (cp_walk_tree (&cond, find_any_await, &found, NULL)) | |
2888 | { | |
2889 | tree cond_type = TREE_TYPE (cond); | |
2890 | tree cond_var = build_lang_decl (VAR_DECL, NULL_TREE, cond_type); | |
2891 | DECL_ARTIFICIAL (cond_var) = true; | |
2892 | layout_decl (cond_var, 0); | |
2893 | gcc_checking_assert (!TYPE_NEEDS_CONSTRUCTING (cond_type)); | |
6ffbf87c | 2894 | cond = cp_build_init_expr (cond_var, cond); |
0f66b848 IS |
2895 | var_nest_node *ins |
2896 | = new var_nest_node (cond_var, cond, n->prev, n); | |
2897 | COND_EXPR_COND (n->init) = cond_var; | |
2898 | flatten_await_stmt (ins, promoted, temps_used, NULL); | |
2899 | } | |
7f327e87 | 2900 | |
0f66b848 IS |
2901 | n->then_cl |
2902 | = new var_nest_node (n->var, COND_EXPR_THEN (n->init), NULL, NULL); | |
2903 | n->else_cl | |
2904 | = new var_nest_node (n->var, COND_EXPR_ELSE (n->init), NULL, NULL); | |
2905 | flatten_await_stmt (n->then_cl, promoted, temps_used, NULL); | |
2906 | /* Point to the start of the flattened code. */ | |
2907 | while (n->then_cl->prev) | |
2908 | n->then_cl = n->then_cl->prev; | |
2909 | flatten_await_stmt (n->else_cl, promoted, temps_used, NULL); | |
2910 | while (n->else_cl->prev) | |
2911 | n->else_cl = n->else_cl->prev; | |
2912 | return; | |
2913 | } | |
2914 | break; | |
2915 | } | |
2916 | coro_interesting_subtree v = { NULL, temps_used }; | |
2917 | tree t = cp_walk_tree (&n->init, find_interesting_subtree, (void *)&v, NULL); | |
2918 | if (!t) | |
2919 | return; | |
2920 | switch (TREE_CODE (t)) | |
2921 | { | |
2922 | default: break; | |
2923 | case CO_AWAIT_EXPR: | |
49789fd0 | 2924 | { |
0f66b848 IS |
2925 | /* Await expressions with initializers have a compiler-temporary |
2926 | as the awaitable. 'promote' this. */ | |
2927 | tree var = TREE_OPERAND (t, 1); | |
2928 | bool already_present = promoted->add (var); | |
2929 | gcc_checking_assert (!already_present); | |
2930 | tree init = TREE_OPERAND (t, 2); | |
2931 | switch (TREE_CODE (init)) | |
2932 | { | |
2933 | default: break; | |
2934 | case INIT_EXPR: | |
2935 | case MODIFY_EXPR: | |
2936 | { | |
2937 | tree inner = TREE_OPERAND (init, 1); | |
2938 | /* We can have non-lvalue-expressions here, but when we see | |
2939 | a target expression, mark it as already used. */ | |
2940 | if (TREE_CODE (inner) == TARGET_EXPR) | |
2941 | { | |
2942 | temps_used->add (inner); | |
2943 | gcc_checking_assert | |
1fea6f82 | 2944 | (TREE_CODE (TARGET_EXPR_INITIAL (inner)) != COND_EXPR); |
0f66b848 IS |
2945 | } |
2946 | } | |
2947 | break; | |
2948 | case CALL_EXPR: | |
2949 | /* If this is a call and not a CTOR, then we didn't expect it. */ | |
2950 | gcc_checking_assert | |
2951 | (DECL_CONSTRUCTOR_P (TREE_OPERAND (CALL_EXPR_FN (init), 0))); | |
2952 | break; | |
2953 | } | |
2954 | var_nest_node *ins = new var_nest_node (var, init, n->prev, n); | |
2955 | TREE_OPERAND (t, 2) = NULL_TREE; | |
2956 | flatten_await_stmt (ins, promoted, temps_used, NULL); | |
2957 | flatten_await_stmt (n, promoted, temps_used, NULL); | |
2958 | return; | |
49789fd0 | 2959 | } |
0f66b848 IS |
2960 | break; |
2961 | case TARGET_EXPR: | |
49789fd0 | 2962 | { |
ed819846 IS |
2963 | /* We have a temporary; promote it, but allow for the idiom in code |
2964 | generated by the compiler like | |
2965 | a = (target_expr produces temp, op uses temp). */ | |
0f66b848 IS |
2966 | tree init = t; |
2967 | temps_used->add (init); | |
2968 | tree var_type = TREE_TYPE (init); | |
fc4cde2e | 2969 | char *buf = xasprintf ("T%03u", (unsigned) temps_used->elements ()); |
0f66b848 IS |
2970 | tree var = build_lang_decl (VAR_DECL, get_identifier (buf), var_type); |
2971 | DECL_ARTIFICIAL (var) = true; | |
2972 | free (buf); | |
2973 | bool already_present = promoted->add (var); | |
2974 | gcc_checking_assert (!already_present); | |
1fea6f82 | 2975 | tree inner = TARGET_EXPR_INITIAL (init); |
0f66b848 | 2976 | gcc_checking_assert (TREE_CODE (inner) != COND_EXPR); |
14ed21f8 JM |
2977 | init = cp_build_modify_expr (input_location, var, INIT_EXPR, init, |
2978 | tf_warning_or_error); | |
ed819846 IS |
2979 | /* Simplify for the case that we have an init containing the temp |
2980 | alone. */ | |
2981 | if (t == n->init && n->var == NULL_TREE) | |
2982 | { | |
2983 | n->var = var; | |
1fea6f82 | 2984 | proxy_replace pr = {TARGET_EXPR_SLOT (t), var}; |
ed819846 IS |
2985 | cp_walk_tree (&init, replace_proxy, &pr, NULL); |
2986 | n->init = init; | |
2987 | if (replace_in) | |
2988 | cp_walk_tree (replace_in, replace_proxy, &pr, NULL); | |
2989 | flatten_await_stmt (n, promoted, temps_used, NULL); | |
2990 | } | |
2991 | else | |
2992 | { | |
2993 | var_nest_node *ins | |
2994 | = new var_nest_node (var, init, n->prev, n); | |
2995 | /* We have to replace the target expr... */ | |
2996 | *v.entry = var; | |
2997 | /* ... and any uses of its var. */ | |
1fea6f82 | 2998 | proxy_replace pr = {TARGET_EXPR_SLOT (t), var}; |
ed819846 IS |
2999 | cp_walk_tree (&n->init, replace_proxy, &pr, NULL); |
3000 | /* Compiler-generated temporaries can also have uses in | |
3001 | following arms of compound expressions, which will be listed | |
3002 | in 'replace_in' if present. */ | |
3003 | if (replace_in) | |
3004 | cp_walk_tree (replace_in, replace_proxy, &pr, NULL); | |
3005 | flatten_await_stmt (ins, promoted, temps_used, NULL); | |
3006 | flatten_await_stmt (n, promoted, temps_used, NULL); | |
3007 | } | |
0f66b848 | 3008 | return; |
49789fd0 | 3009 | } |
0f66b848 | 3010 | break; |
49789fd0 | 3011 | } |
49789fd0 IS |
3012 | } |
3013 | ||
0f66b848 IS |
3014 | /* Helper for 'process_conditional' that handles recursion into nested |
3015 | conditionals. */ | |
49789fd0 | 3016 | |
0f66b848 IS |
3017 | static void |
3018 | handle_nested_conditionals (var_nest_node *n, vec<tree>& list, | |
3019 | hash_map<tree, tree>& map) | |
49789fd0 | 3020 | { |
0f66b848 IS |
3021 | do |
3022 | { | |
3023 | if (n->var && DECL_NAME (n->var)) | |
3024 | { | |
3025 | list.safe_push (n->var); | |
3026 | if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (n->var))) | |
3027 | { | |
3028 | bool existed; | |
3029 | tree& flag = map.get_or_insert (n->var, &existed); | |
3030 | if (!existed) | |
3031 | { | |
3032 | /* We didn't see this var before and it needs a DTOR, so | |
3033 | build a guard variable for it. */ | |
3034 | char *nam | |
3035 | = xasprintf ("%s_guard", | |
3036 | IDENTIFIER_POINTER (DECL_NAME (n->var))); | |
3037 | flag = build_lang_decl (VAR_DECL, get_identifier (nam), | |
3038 | boolean_type_node); | |
3039 | free (nam); | |
3040 | DECL_ARTIFICIAL (flag) = true; | |
3041 | } | |
3042 | ||
3043 | /* The initializer for this variable is replaced by a compound | |
3044 | expression that performs the init and then records that the | |
3045 | variable is live (and the DTOR should be run at the scope | |
3046 | exit. */ | |
6ffbf87c | 3047 | tree set_flag = cp_build_init_expr (flag, boolean_true_node); |
0f66b848 IS |
3048 | n->init |
3049 | = build2 (COMPOUND_EXPR, boolean_type_node, n->init, set_flag); | |
3050 | } | |
3051 | } | |
3052 | if (TREE_CODE (n->init) == COND_EXPR) | |
3053 | { | |
3054 | tree new_then = push_stmt_list (); | |
3055 | handle_nested_conditionals (n->then_cl, list, map); | |
3056 | new_then = pop_stmt_list (new_then); | |
3057 | tree new_else = push_stmt_list (); | |
3058 | handle_nested_conditionals (n->else_cl, list, map); | |
3059 | new_else = pop_stmt_list (new_else); | |
3060 | tree new_if | |
3061 | = build4 (IF_STMT, void_type_node, COND_EXPR_COND (n->init), | |
3062 | new_then, new_else, NULL_TREE); | |
3063 | add_stmt (new_if); | |
3064 | } | |
3065 | else | |
3066 | finish_expr_stmt (n->init); | |
3067 | n = n->next; | |
3068 | } while (n); | |
3069 | } | |
49789fd0 | 3070 | |
0f66b848 | 3071 | /* helper for 'maybe_promote_temps'. |
f1656ae9 | 3072 | |
0f66b848 IS |
3073 | When we have a conditional expression which might embed await expressions |
3074 | and/or promoted variables, we need to handle it appropriately. | |
49789fd0 | 3075 | |
0f66b848 IS |
3076 | The linked lists for the 'then' and 'else' clauses in a conditional node |
3077 | identify the promoted variables (but these cannot be wrapped in a regular | |
3078 | cleanup). | |
49789fd0 | 3079 | |
0f66b848 IS |
3080 | So recurse through the lists and build up a composite list of captured vars. |
3081 | Declare these and any guard variables needed to decide if a DTOR should be | |
3082 | run. Then embed the conditional into a try-finally expression that handles | |
3083 | running each DTOR conditionally on its guard variable. */ | |
3084 | ||
3085 | static void | |
3086 | process_conditional (var_nest_node *n, tree& vlist) | |
3087 | { | |
3088 | tree init = n->init; | |
3089 | hash_map<tree, tree> var_flags; | |
91bb571d | 3090 | auto_vec<tree> var_list; |
0f66b848 IS |
3091 | tree new_then = push_stmt_list (); |
3092 | handle_nested_conditionals (n->then_cl, var_list, var_flags); | |
3093 | new_then = pop_stmt_list (new_then); | |
3094 | tree new_else = push_stmt_list (); | |
3095 | handle_nested_conditionals (n->else_cl, var_list, var_flags); | |
3096 | new_else = pop_stmt_list (new_else); | |
3097 | /* Declare the vars. There are two loops so that the boolean flags are | |
3098 | grouped in the frame. */ | |
3099 | for (unsigned i = 0; i < var_list.length(); i++) | |
005530eb | 3100 | { |
0f66b848 IS |
3101 | tree var = var_list[i]; |
3102 | DECL_CHAIN (var) = vlist; | |
3103 | vlist = var; | |
3104 | add_decl_expr (var); | |
daaed019 | 3105 | } |
0f66b848 IS |
3106 | /* Define the guard flags for variables that need a DTOR. */ |
3107 | for (unsigned i = 0; i < var_list.length(); i++) | |
3108 | { | |
3109 | tree *flag = var_flags.get (var_list[i]); | |
3110 | if (flag) | |
3111 | { | |
3112 | DECL_INITIAL (*flag) = boolean_false_node; | |
3113 | DECL_CHAIN (*flag) = vlist; | |
3114 | vlist = *flag; | |
3115 | add_decl_expr (*flag); | |
3116 | } | |
3117 | } | |
3118 | tree new_if | |
3119 | = build4 (IF_STMT, void_type_node, COND_EXPR_COND (init), | |
3120 | new_then, new_else, NULL_TREE); | |
3121 | /* Build a set of conditional DTORs. */ | |
3122 | tree final_actions = push_stmt_list (); | |
3123 | while (!var_list.is_empty()) | |
3124 | { | |
3125 | tree var = var_list.pop (); | |
3126 | tree *flag = var_flags.get (var); | |
3127 | if (!flag) | |
3128 | continue; | |
44868e72 JM |
3129 | if (tree cleanup = cxx_maybe_build_cleanup (var, tf_warning_or_error)) |
3130 | { | |
3131 | tree cond_cleanup = begin_if_stmt (); | |
3132 | finish_if_stmt_cond (*flag, cond_cleanup); | |
3133 | finish_expr_stmt (cleanup); | |
3134 | finish_then_clause (cond_cleanup); | |
3135 | finish_if_stmt (cond_cleanup); | |
3136 | } | |
0f66b848 IS |
3137 | } |
3138 | final_actions = pop_stmt_list (final_actions); | |
3139 | tree try_finally | |
3140 | = build2 (TRY_FINALLY_EXPR, void_type_node, new_if, final_actions); | |
3141 | add_stmt (try_finally); | |
3142 | } | |
aa94a22f | 3143 | |
0f66b848 | 3144 | /* Given *STMT, that contains at least one await expression. |
49789fd0 | 3145 | |
0f66b848 IS |
3146 | The full expression represented in the original source code will contain |
3147 | suspension points, but it is still required that the lifetime of temporary | |
3148 | values extends to the end of the expression. | |
49789fd0 | 3149 | |
0f66b848 IS |
3150 | We already have a mechanism to 'promote' user-authored local variables |
3151 | to a coroutine frame counterpart (which allows explicit management of the | |
3152 | lifetime across suspensions). The transform here re-writes STMT into | |
3153 | a bind expression, promotes temporary values into local variables in that | |
3154 | and flattens the statement into a series of cleanups. | |
49789fd0 | 3155 | |
0f66b848 IS |
3156 | Conditional expressions are re-written to regular 'if' statements. |
3157 | The cleanups for variables initialized inside a conditional (including | |
3158 | nested cases) are wrapped in a try-finally clause, with guard variables | |
3159 | to determine which DTORs need to be run. */ | |
0666767e | 3160 | |
0f66b848 IS |
3161 | static tree |
3162 | maybe_promote_temps (tree *stmt, void *d) | |
0666767e IS |
3163 | { |
3164 | susp_frame_data *awpts = (susp_frame_data *) d; | |
0f66b848 | 3165 | |
0666767e | 3166 | location_t sloc = EXPR_LOCATION (*stmt); |
0f66b848 IS |
3167 | tree expr = *stmt; |
3168 | /* Strip off uninteresting wrappers. */ | |
3169 | if (TREE_CODE (expr) == CLEANUP_POINT_EXPR) | |
3170 | expr = TREE_OPERAND (expr, 0); | |
3171 | if (TREE_CODE (expr) == EXPR_STMT) | |
3172 | expr = EXPR_STMT_EXPR (expr); | |
3173 | if (TREE_CODE (expr) == CONVERT_EXPR | |
3174 | && VOID_TYPE_P (TREE_TYPE (expr))) | |
3175 | expr = TREE_OPERAND (expr, 0); | |
3176 | STRIP_NOPS (expr); | |
3177 | ||
3178 | /* We walk the statement trees, flattening it into an ordered list of | |
3179 | variables with initializers and fragments corresponding to compound | |
3180 | expressions, truth or/and if and ternary conditionals. Conditional | |
3181 | expressions carry a nested list of fragments for the then and else | |
3182 | clauses. We anchor to the 'bottom' of the fragment list; we will write | |
3183 | a cleanup nest with one shell for each variable initialized. */ | |
3184 | var_nest_node *root = new var_nest_node (NULL_TREE, expr, NULL, NULL); | |
3185 | /* Check to see we didn't promote one twice. */ | |
3186 | hash_set<tree> promoted_vars; | |
3187 | hash_set<tree> used_temps; | |
3188 | flatten_await_stmt (root, &promoted_vars, &used_temps, NULL); | |
3189 | ||
3190 | gcc_checking_assert (root->next == NULL); | |
3191 | tree vlist = NULL_TREE; | |
3192 | var_nest_node *t = root; | |
0f66b848 IS |
3193 | /* We build the bind scope expression from the bottom-up. |
3194 | EXPR_LIST holds the inner expression nest at the current cleanup | |
3195 | level (becoming the final expression list when we've exhausted the | |
3196 | number of sub-expression fragments). */ | |
3197 | tree expr_list = NULL_TREE; | |
3198 | do | |
0666767e | 3199 | { |
0f66b848 IS |
3200 | tree new_list = push_stmt_list (); |
3201 | /* When we have a promoted variable, then add that to the bind scope | |
3202 | and initialize it. When there's no promoted variable, we just need | |
3203 | to run the initializer. | |
3204 | If the initializer is a conditional expression, we need to collect | |
3205 | and declare any promoted variables nested within it. DTORs for such | |
3206 | variables must be run conditionally too. */ | |
15a176a8 | 3207 | if (t->var) |
0666767e | 3208 | { |
0f66b848 IS |
3209 | tree var = t->var; |
3210 | DECL_CHAIN (var) = vlist; | |
3211 | vlist = var; | |
3212 | add_decl_expr (var); | |
3213 | if (TREE_CODE (t->init) == COND_EXPR) | |
3214 | process_conditional (t, vlist); | |
3215 | else | |
3216 | finish_expr_stmt (t->init); | |
44868e72 | 3217 | if (tree cleanup = cxx_maybe_build_cleanup (var, tf_warning_or_error)) |
0f66b848 | 3218 | { |
0f66b848 IS |
3219 | tree cl = build_stmt (sloc, CLEANUP_STMT, expr_list, cleanup, var); |
3220 | add_stmt (cl); /* push this onto the level above. */ | |
3221 | } | |
3222 | else if (expr_list) | |
ed819846 IS |
3223 | { |
3224 | if (TREE_CODE (expr_list) != STATEMENT_LIST) | |
3225 | add_stmt (expr_list); | |
3226 | else if (!tsi_end_p (tsi_start (expr_list))) | |
3227 | add_stmt (expr_list); | |
3228 | } | |
0666767e IS |
3229 | } |
3230 | else | |
0f66b848 IS |
3231 | { |
3232 | if (TREE_CODE (t->init) == COND_EXPR) | |
3233 | process_conditional (t, vlist); | |
3234 | else | |
3235 | finish_expr_stmt (t->init); | |
3236 | if (expr_list) | |
ed819846 IS |
3237 | { |
3238 | if (TREE_CODE (expr_list) != STATEMENT_LIST) | |
3239 | add_stmt (expr_list); | |
3240 | else if (!tsi_end_p (tsi_start (expr_list))) | |
3241 | add_stmt (expr_list); | |
3242 | } | |
0f66b848 IS |
3243 | } |
3244 | expr_list = pop_stmt_list (new_list); | |
3245 | var_nest_node *old = t; | |
3246 | t = t->prev; | |
3247 | delete old; | |
3248 | } while (t); | |
3249 | ||
3250 | /* Now produce the bind expression containing the 'promoted' temporaries | |
3251 | as its variable list, and the cleanup nest as the statement. */ | |
3252 | tree await_bind = build3_loc (sloc, BIND_EXPR, void_type_node, | |
3253 | NULL, NULL, NULL); | |
3254 | BIND_EXPR_BODY (await_bind) = expr_list; | |
3255 | BIND_EXPR_VARS (await_bind) = nreverse (vlist); | |
0666767e IS |
3256 | tree b_block = make_node (BLOCK); |
3257 | if (!awpts->block_stack->is_empty ()) | |
3258 | { | |
3259 | tree s_block = awpts->block_stack->last (); | |
3260 | if (s_block) | |
3261 | { | |
3262 | BLOCK_SUPERCONTEXT (b_block) = s_block; | |
3263 | BLOCK_CHAIN (b_block) = BLOCK_SUBBLOCKS (s_block); | |
3264 | BLOCK_SUBBLOCKS (s_block) = b_block; | |
3265 | } | |
3266 | } | |
0f66b848 IS |
3267 | BLOCK_VARS (b_block) = BIND_EXPR_VARS (await_bind) ; |
3268 | BIND_EXPR_BLOCK (await_bind) = b_block; | |
3269 | TREE_SIDE_EFFECTS (await_bind) = TREE_SIDE_EFFECTS (BIND_EXPR_BODY (await_bind)); | |
3270 | *stmt = await_bind; | |
49789fd0 | 3271 | hash_set<tree> visited; |
0f66b848 | 3272 | return cp_walk_tree (stmt, register_awaits, d, &visited); |
49789fd0 IS |
3273 | } |
3274 | ||
3dbc7721 IS |
3275 | /* Lightweight callback to determine two key factors: |
3276 | 1) If the statement/expression contains any await expressions. | |
3277 | 2) If the statement/expression potentially requires a re-write to handle | |
3278 | TRUTH_{AND,OR}IF_EXPRs since, in most cases, they will need expansion | |
3279 | so that the await expressions are not processed in the case of the | |
3280 | short-circuit arm. | |
0f66b848 | 3281 | |
3dbc7721 IS |
3282 | CO_YIELD expressions are re-written to their underlying co_await. */ |
3283 | ||
3284 | static tree | |
3285 | analyze_expression_awaits (tree *stmt, int *do_subtree, void *d) | |
3286 | { | |
3287 | susp_frame_data *awpts = (susp_frame_data *) d; | |
3288 | ||
3289 | switch (TREE_CODE (*stmt)) | |
3290 | { | |
3291 | default: return NULL_TREE; | |
3292 | case CO_YIELD_EXPR: | |
3293 | /* co_yield is syntactic sugar, re-write it to co_await. */ | |
3294 | *stmt = TREE_OPERAND (*stmt, 1); | |
3295 | /* FALLTHROUGH */ | |
3296 | case CO_AWAIT_EXPR: | |
3297 | awpts->saw_awaits++; | |
0f66b848 IS |
3298 | /* A non-null initializer for the awaiter means we need to expand. */ |
3299 | if (TREE_OPERAND (*stmt, 2)) | |
3300 | awpts->has_awaiter_init = true; | |
3dbc7721 IS |
3301 | break; |
3302 | case TRUTH_ANDIF_EXPR: | |
3303 | case TRUTH_ORIF_EXPR: | |
3304 | { | |
3305 | /* We don't need special action for awaits in the always-executed | |
3306 | arm of a TRUTH_IF. */ | |
3307 | if (tree res = cp_walk_tree (&TREE_OPERAND (*stmt, 0), | |
3308 | analyze_expression_awaits, d, NULL)) | |
3309 | return res; | |
3310 | /* However, if there are await expressions on the conditionally | |
3311 | executed branch, we must expand the TRUTH_IF to ensure that the | |
3312 | expanded await expression control-flow is fully contained in the | |
3313 | conditionally executed code. */ | |
3314 | unsigned aw_count = awpts->saw_awaits; | |
3315 | if (tree res = cp_walk_tree (&TREE_OPERAND (*stmt, 1), | |
3316 | analyze_expression_awaits, d, NULL)) | |
3317 | return res; | |
3318 | if (awpts->saw_awaits > aw_count) | |
3319 | { | |
3320 | awpts->truth_aoif_to_expand->add (*stmt); | |
3321 | awpts->needs_truth_if_exp = true; | |
3322 | } | |
3323 | /* We've done the sub-trees here. */ | |
3324 | *do_subtree = 0; | |
3325 | } | |
3326 | break; | |
3327 | } | |
3328 | ||
3329 | return NULL_TREE; /* Recurse until done. */ | |
3330 | } | |
3331 | ||
3332 | /* Given *EXPR | |
3333 | If EXPR contains a TRUTH_{AND,OR}IF_EXPR, TAOIE with an await expr on | |
0f66b848 | 3334 | the conditionally executed branch, change this in a ternary operator. |
3dbc7721 IS |
3335 | |
3336 | bool not_expr = TAOIE == TRUTH_ORIF_EXPR ? NOT : NOP; | |
0f66b848 | 3337 | not_expr (always-exec expr) ? conditionally-exec expr : not_expr; |
3dbc7721 | 3338 | |
0f66b848 IS |
3339 | Apply this recursively to the condition and the conditionally-exec |
3340 | branch. */ | |
3dbc7721 IS |
3341 | |
3342 | struct truth_if_transform { | |
3343 | tree *orig_stmt; | |
3344 | tree scratch_var; | |
3345 | hash_set<tree> *truth_aoif_to_expand; | |
3346 | }; | |
3347 | ||
3348 | static tree | |
3349 | expand_one_truth_if (tree *expr, int *do_subtree, void *d) | |
3350 | { | |
3351 | truth_if_transform *xform = (truth_if_transform *) d; | |
3352 | ||
3353 | bool needs_not = false; | |
3354 | switch (TREE_CODE (*expr)) | |
3355 | { | |
3356 | default: break; | |
3357 | case TRUTH_ORIF_EXPR: | |
3358 | needs_not = true; | |
3359 | /* FALLTHROUGH */ | |
3360 | case TRUTH_ANDIF_EXPR: | |
3361 | { | |
3362 | if (!xform->truth_aoif_to_expand->contains (*expr)) | |
3363 | break; | |
3364 | ||
3365 | location_t sloc = EXPR_LOCATION (*expr); | |
0f66b848 IS |
3366 | /* Transform truth expression into a cond expression with |
3367 | * the always-executed arm as the condition. | |
3368 | * the conditionally-executed arm as the then clause. | |
3369 | * the 'else' clause is fixed: 'true' for ||,'false' for &&. */ | |
3370 | tree cond = TREE_OPERAND (*expr, 0); | |
3371 | tree test1 = TREE_OPERAND (*expr, 1); | |
3372 | tree fixed = needs_not ? boolean_true_node : boolean_false_node; | |
3dbc7721 | 3373 | if (needs_not) |
0f66b848 IS |
3374 | cond = build1 (TRUTH_NOT_EXPR, boolean_type_node, cond); |
3375 | tree cond_expr | |
3376 | = build3_loc (sloc, COND_EXPR, boolean_type_node, | |
3377 | cond, test1, fixed); | |
3378 | *expr = cond_expr; | |
3379 | if (tree res = cp_walk_tree (&COND_EXPR_COND (*expr), | |
3380 | expand_one_truth_if, d, NULL)) | |
3dbc7721 | 3381 | return res; |
0f66b848 IS |
3382 | if (tree res = cp_walk_tree (&COND_EXPR_THEN (*expr), |
3383 | expand_one_truth_if, d, NULL)) | |
3dbc7721 | 3384 | return res; |
0f66b848 | 3385 | /* We've manually processed necessary sub-trees here. */ |
3dbc7721 IS |
3386 | *do_subtree = 0; |
3387 | } | |
3388 | break; | |
3389 | } | |
3390 | return NULL_TREE; | |
3391 | } | |
3392 | ||
3393 | /* Helper that adds a new variable of VAR_TYPE to a bind scope BIND, the | |
3394 | name is made up from NAM_ROOT, NAM_VERS. */ | |
3395 | ||
3396 | static tree | |
3397 | add_var_to_bind (tree& bind, tree var_type, | |
3398 | const char *nam_root, unsigned nam_vers) | |
3399 | { | |
3dbc7721 IS |
3400 | tree b_vars = BIND_EXPR_VARS (bind); |
3401 | /* Build a variable to hold the condition, this will be included in the | |
3402 | frame as a local var. */ | |
15a176a8 | 3403 | char *nam = xasprintf ("__%s_%d", nam_root, nam_vers); |
3dbc7721 IS |
3404 | tree newvar = build_lang_decl (VAR_DECL, get_identifier (nam), var_type); |
3405 | free (nam); | |
3406 | DECL_CHAIN (newvar) = b_vars; | |
3407 | BIND_EXPR_VARS (bind) = newvar; | |
3408 | return newvar; | |
3409 | } | |
3410 | ||
3411 | /* Helper to build and add if (!cond) break; */ | |
3412 | ||
3413 | static void | |
3414 | coro_build_add_if_not_cond_break (tree cond) | |
3415 | { | |
3416 | tree if_stmt = begin_if_stmt (); | |
3417 | tree invert = build1 (TRUTH_NOT_EXPR, boolean_type_node, cond); | |
3418 | finish_if_stmt_cond (invert, if_stmt); | |
3419 | finish_break_stmt (); | |
3420 | finish_then_clause (if_stmt); | |
3421 | finish_if_stmt (if_stmt); | |
3422 | } | |
3423 | ||
26e0eb10 IS |
3424 | /* Tree walk callback to replace continue statements with goto label. */ |
3425 | static tree | |
3426 | replace_continue (tree *stmt, int *do_subtree, void *d) | |
3427 | { | |
3428 | tree expr = *stmt; | |
3429 | if (TREE_CODE (expr) == CLEANUP_POINT_EXPR) | |
3430 | expr = TREE_OPERAND (expr, 0); | |
370a0dee | 3431 | if (CONVERT_EXPR_P (expr) && VOID_TYPE_P (TREE_TYPE (expr))) |
26e0eb10 IS |
3432 | expr = TREE_OPERAND (expr, 0); |
3433 | STRIP_NOPS (expr); | |
3434 | if (!STATEMENT_CLASS_P (expr)) | |
3435 | return NULL_TREE; | |
3436 | ||
3437 | switch (TREE_CODE (expr)) | |
3438 | { | |
3439 | /* Unless it's a special case, just walk the subtrees as usual. */ | |
3440 | default: return NULL_TREE; | |
3441 | ||
3442 | case CONTINUE_STMT: | |
3443 | { | |
3444 | tree *label = (tree *)d; | |
3445 | location_t loc = EXPR_LOCATION (expr); | |
3446 | /* re-write a continue to goto label. */ | |
3447 | *stmt = build_stmt (loc, GOTO_EXPR, *label); | |
3448 | *do_subtree = 0; | |
3449 | return NULL_TREE; | |
3450 | } | |
3451 | ||
3452 | /* Statements that do not require recursion. */ | |
3453 | case DECL_EXPR: | |
3454 | case BREAK_STMT: | |
3455 | case GOTO_EXPR: | |
3456 | case LABEL_EXPR: | |
3457 | case CASE_LABEL_EXPR: | |
3458 | case ASM_EXPR: | |
3459 | /* These must break recursion. */ | |
3460 | case FOR_STMT: | |
3461 | case WHILE_STMT: | |
3462 | case DO_STMT: | |
3463 | *do_subtree = 0; | |
3464 | return NULL_TREE; | |
3465 | } | |
3466 | } | |
3467 | ||
3dbc7721 IS |
3468 | /* Tree walk callback to analyze, register and pre-process statements that |
3469 | contain await expressions. */ | |
3470 | ||
49789fd0 IS |
3471 | static tree |
3472 | await_statement_walker (tree *stmt, int *do_subtree, void *d) | |
3473 | { | |
3474 | tree res = NULL_TREE; | |
3475 | susp_frame_data *awpts = (susp_frame_data *) d; | |
3476 | ||
0666767e | 3477 | /* Process a statement at a time. */ |
49789fd0 IS |
3478 | if (TREE_CODE (*stmt) == BIND_EXPR) |
3479 | { | |
3dbc7721 IS |
3480 | /* For conditional expressions, we might wish to add an artificial var |
3481 | to their containing bind expr. */ | |
3482 | vec_safe_push (awpts->bind_stack, *stmt); | |
0666767e IS |
3483 | /* We might need to insert a new bind expression, and want to link it |
3484 | into the correct scope, so keep a note of the current block scope. */ | |
49789fd0 IS |
3485 | tree blk = BIND_EXPR_BLOCK (*stmt); |
3486 | vec_safe_push (awpts->block_stack, blk); | |
0666767e IS |
3487 | res = cp_walk_tree (&BIND_EXPR_BODY (*stmt), await_statement_walker, |
3488 | d, NULL); | |
3489 | awpts->block_stack->pop (); | |
3dbc7721 | 3490 | awpts->bind_stack->pop (); |
0666767e | 3491 | *do_subtree = 0; /* Done subtrees. */ |
3dbc7721 | 3492 | return res; |
0666767e IS |
3493 | } |
3494 | else if (TREE_CODE (*stmt) == STATEMENT_LIST) | |
3495 | { | |
0f54cc9c | 3496 | for (tree &s : tsi_range (*stmt)) |
49789fd0 | 3497 | { |
0f54cc9c | 3498 | res = cp_walk_tree (&s, await_statement_walker, |
0666767e IS |
3499 | d, NULL); |
3500 | if (res) | |
3501 | return res; | |
49789fd0 | 3502 | } |
0666767e | 3503 | *do_subtree = 0; /* Done subtrees. */ |
3dbc7721 | 3504 | return NULL_TREE; |
49789fd0 | 3505 | } |
3dbc7721 | 3506 | |
ab08859e IS |
3507 | /* We have something to be handled as a single statement. We have to handle |
3508 | a few statements specially where await statements have to be moved out of | |
3509 | constructs. */ | |
0f66b848 | 3510 | tree expr = *stmt; |
ab08859e | 3511 | if (TREE_CODE (*stmt) == CLEANUP_POINT_EXPR) |
0f66b848 IS |
3512 | expr = TREE_OPERAND (expr, 0); |
3513 | STRIP_NOPS (expr); | |
3514 | ||
3515 | if (STATEMENT_CLASS_P (expr)) | |
3516 | switch (TREE_CODE (expr)) | |
3dbc7721 IS |
3517 | { |
3518 | /* Unless it's a special case, just walk the subtrees as usual. */ | |
3519 | default: return NULL_TREE; | |
3520 | ||
3521 | /* When we have a conditional expression, which contains one or more | |
3522 | await expressions, we have to break the condition out into a | |
3523 | regular statement so that the control flow introduced by the await | |
3524 | transforms can be implemented. */ | |
3525 | case IF_STMT: | |
3526 | { | |
ab08859e IS |
3527 | tree *await_ptr; |
3528 | hash_set<tree> visited; | |
3dbc7721 IS |
3529 | /* Transform 'if (cond with awaits) then stmt1 else stmt2' into |
3530 | bool cond = cond with awaits. | |
3531 | if (cond) then stmt1 else stmt2. */ | |
3532 | tree if_stmt = *stmt; | |
3533 | /* We treat the condition as if it was a stand-alone statement, | |
a2086f98 | 3534 | to see if there are any await expressions which will be analyzed |
3dbc7721 | 3535 | and registered. */ |
ab08859e IS |
3536 | if (!(cp_walk_tree (&IF_COND (if_stmt), |
3537 | find_any_await, &await_ptr, &visited))) | |
3dbc7721 IS |
3538 | return NULL_TREE; /* Nothing special to do here. */ |
3539 | ||
3540 | gcc_checking_assert (!awpts->bind_stack->is_empty()); | |
3541 | tree& bind_expr = awpts->bind_stack->last (); | |
3542 | tree newvar = add_var_to_bind (bind_expr, boolean_type_node, | |
3543 | "ifcd", awpts->cond_number++); | |
3544 | tree insert_list = push_stmt_list (); | |
3545 | tree cond_inner = IF_COND (if_stmt); | |
3546 | if (TREE_CODE (cond_inner) == CLEANUP_POINT_EXPR) | |
3547 | cond_inner = TREE_OPERAND (cond_inner, 0); | |
3548 | add_decl_expr (newvar); | |
3549 | location_t sloc = EXPR_LOCATION (IF_COND (if_stmt)); | |
3550 | /* We want to initialize the new variable with the expression | |
3551 | that contains the await(s) and potentially also needs to | |
3552 | have truth_if expressions expanded. */ | |
6ffbf87c | 3553 | tree new_s = cp_build_init_expr (sloc, newvar, cond_inner); |
3dbc7721 | 3554 | finish_expr_stmt (new_s); |
3dbc7721 IS |
3555 | IF_COND (if_stmt) = newvar; |
3556 | add_stmt (if_stmt); | |
3557 | *stmt = pop_stmt_list (insert_list); | |
3558 | /* So now walk the new statement list. */ | |
3559 | res = cp_walk_tree (stmt, await_statement_walker, d, NULL); | |
3560 | *do_subtree = 0; /* Done subtrees. */ | |
3561 | return res; | |
3562 | } | |
3563 | break; | |
26e0eb10 IS |
3564 | case FOR_STMT: |
3565 | { | |
ab08859e IS |
3566 | tree *await_ptr; |
3567 | hash_set<tree> visited; | |
26e0eb10 IS |
3568 | /* for loops only need special treatment if the condition or the |
3569 | iteration expression contain a co_await. */ | |
3570 | tree for_stmt = *stmt; | |
ab08859e IS |
3571 | /* At present, the FE always generates a separate initializer for |
3572 | the FOR_INIT_STMT, when the expression has an await. Check that | |
3573 | this assumption holds in the future. */ | |
3574 | gcc_checking_assert | |
3575 | (!(cp_walk_tree (&FOR_INIT_STMT (for_stmt), find_any_await, | |
3576 | &await_ptr, &visited))); | |
3577 | ||
3578 | visited.empty (); | |
3579 | bool for_cond_await | |
3580 | = cp_walk_tree (&FOR_COND (for_stmt), find_any_await, | |
3581 | &await_ptr, &visited); | |
3582 | ||
3583 | visited.empty (); | |
3584 | bool for_expr_await | |
3585 | = cp_walk_tree (&FOR_EXPR (for_stmt), find_any_await, | |
3586 | &await_ptr, &visited); | |
26e0eb10 IS |
3587 | |
3588 | /* If the condition has an await, then we will need to rewrite the | |
3589 | loop as | |
3590 | for (init expression;true;iteration expression) { | |
3591 | condition = await expression; | |
3592 | if (condition) | |
3593 | break; | |
3594 | ... | |
3595 | } | |
3596 | */ | |
3597 | if (for_cond_await) | |
3598 | { | |
3599 | tree insert_list = push_stmt_list (); | |
3600 | /* This will be expanded when the revised body is handled. */ | |
3601 | coro_build_add_if_not_cond_break (FOR_COND (for_stmt)); | |
3602 | /* .. add the original for body. */ | |
3603 | add_stmt (FOR_BODY (for_stmt)); | |
3604 | /* To make the new for body. */ | |
3605 | FOR_BODY (for_stmt) = pop_stmt_list (insert_list); | |
3606 | FOR_COND (for_stmt) = boolean_true_node; | |
3607 | } | |
3608 | /* If the iteration expression has an await, it's a bit more | |
3609 | tricky. | |
3610 | for (init expression;condition;) { | |
3611 | ... | |
3612 | iteration_expr_label: | |
3613 | iteration expression with await; | |
3614 | } | |
3615 | but, then we will need to re-write any continue statements into | |
3616 | 'goto iteration_expr_label:'. | |
3617 | */ | |
3618 | if (for_expr_await) | |
3619 | { | |
3620 | location_t sloc = EXPR_LOCATION (FOR_EXPR (for_stmt)); | |
3621 | tree insert_list = push_stmt_list (); | |
3622 | /* The original for body. */ | |
3623 | add_stmt (FOR_BODY (for_stmt)); | |
3624 | char *buf = xasprintf ("for.iter.expr.%u", awpts->cond_number++); | |
3625 | tree it_expr_label | |
3626 | = create_named_label_with_ctx (sloc, buf, NULL_TREE); | |
3627 | free (buf); | |
3628 | add_stmt (build_stmt (sloc, LABEL_EXPR, it_expr_label)); | |
ab08859e IS |
3629 | tree for_expr = FOR_EXPR (for_stmt); |
3630 | /* Present the iteration expression as a statement. */ | |
3631 | if (TREE_CODE (for_expr) == CLEANUP_POINT_EXPR) | |
3632 | for_expr = TREE_OPERAND (for_expr, 0); | |
3633 | STRIP_NOPS (for_expr); | |
3634 | finish_expr_stmt (for_expr); | |
26e0eb10 IS |
3635 | FOR_EXPR (for_stmt) = NULL_TREE; |
3636 | FOR_BODY (for_stmt) = pop_stmt_list (insert_list); | |
3637 | /* rewrite continue statements to goto label. */ | |
3638 | hash_set<tree> visited_continue; | |
3639 | if ((res = cp_walk_tree (&FOR_BODY (for_stmt), | |
3640 | replace_continue, &it_expr_label, &visited_continue))) | |
3641 | return res; | |
3642 | } | |
3643 | ||
3644 | /* So now walk the body statement (list), if there were no await | |
3645 | expressions, then this handles the original body - and either | |
3646 | way we will have finished with this statement. */ | |
3647 | res = cp_walk_tree (&FOR_BODY (for_stmt), | |
3648 | await_statement_walker, d, NULL); | |
3649 | *do_subtree = 0; /* Done subtrees. */ | |
3650 | return res; | |
3651 | } | |
3652 | break; | |
3dbc7721 IS |
3653 | case WHILE_STMT: |
3654 | { | |
3655 | /* We turn 'while (cond with awaits) stmt' into | |
3656 | while (true) { | |
3657 | if (!(cond with awaits)) | |
3658 | break; | |
3659 | stmt.. | |
3660 | } */ | |
ab08859e IS |
3661 | tree *await_ptr; |
3662 | hash_set<tree> visited; | |
3dbc7721 | 3663 | tree while_stmt = *stmt; |
ab08859e IS |
3664 | if (!(cp_walk_tree (&WHILE_COND (while_stmt), |
3665 | find_any_await, &await_ptr, &visited))) | |
3dbc7721 IS |
3666 | return NULL_TREE; /* Nothing special to do here. */ |
3667 | ||
3668 | tree insert_list = push_stmt_list (); | |
3669 | coro_build_add_if_not_cond_break (WHILE_COND (while_stmt)); | |
3670 | /* The original while body. */ | |
3671 | add_stmt (WHILE_BODY (while_stmt)); | |
3672 | /* The new while body. */ | |
3673 | WHILE_BODY (while_stmt) = pop_stmt_list (insert_list); | |
3674 | WHILE_COND (while_stmt) = boolean_true_node; | |
3675 | /* So now walk the new statement list. */ | |
3676 | res = cp_walk_tree (&WHILE_BODY (while_stmt), | |
3677 | await_statement_walker, d, NULL); | |
3678 | *do_subtree = 0; /* Done subtrees. */ | |
3679 | return res; | |
3680 | } | |
3681 | break; | |
3682 | case DO_STMT: | |
3683 | { | |
3684 | /* We turn do stmt while (cond with awaits) into: | |
3685 | do { | |
3686 | stmt.. | |
3687 | if (!(cond with awaits)) | |
3688 | break; | |
3689 | } while (true); */ | |
3690 | tree do_stmt = *stmt; | |
ab08859e IS |
3691 | tree *await_ptr; |
3692 | hash_set<tree> visited; | |
3693 | if (!(cp_walk_tree (&DO_COND (do_stmt), | |
3694 | find_any_await, &await_ptr, &visited))) | |
3dbc7721 IS |
3695 | return NULL_TREE; /* Nothing special to do here. */ |
3696 | ||
3697 | tree insert_list = push_stmt_list (); | |
3698 | /* The original do stmt body. */ | |
3699 | add_stmt (DO_BODY (do_stmt)); | |
3700 | coro_build_add_if_not_cond_break (DO_COND (do_stmt)); | |
3701 | /* The new while body. */ | |
3702 | DO_BODY (do_stmt) = pop_stmt_list (insert_list); | |
3703 | DO_COND (do_stmt) = boolean_true_node; | |
3704 | /* So now walk the new statement list. */ | |
3705 | res = cp_walk_tree (&DO_BODY (do_stmt), await_statement_walker, | |
3706 | d, NULL); | |
3707 | *do_subtree = 0; /* Done subtrees. */ | |
3708 | return res; | |
3dbc7721 IS |
3709 | } |
3710 | break; | |
3711 | case SWITCH_STMT: | |
3712 | { | |
3713 | /* We turn 'switch (cond with awaits) stmt' into | |
3714 | switch_type cond = cond with awaits | |
3715 | switch (cond) stmt. */ | |
3716 | tree sw_stmt = *stmt; | |
ab08859e IS |
3717 | tree *await_ptr; |
3718 | hash_set<tree> visited; | |
3719 | if (!(cp_walk_tree (&SWITCH_STMT_COND (sw_stmt), | |
3720 | find_any_await, &await_ptr, &visited))) | |
3dbc7721 IS |
3721 | return NULL_TREE; /* Nothing special to do here. */ |
3722 | ||
3723 | gcc_checking_assert (!awpts->bind_stack->is_empty()); | |
3724 | /* Build a variable to hold the condition, this will be | |
3725 | included in the frame as a local var. */ | |
3726 | tree& bind_expr = awpts->bind_stack->last (); | |
3727 | tree sw_type = SWITCH_STMT_TYPE (sw_stmt); | |
3728 | tree newvar = add_var_to_bind (bind_expr, sw_type, "swch", | |
3729 | awpts->cond_number++); | |
3730 | tree insert_list = push_stmt_list (); | |
3731 | add_decl_expr (newvar); | |
3732 | ||
3733 | tree cond_inner = SWITCH_STMT_COND (sw_stmt); | |
3734 | if (TREE_CODE (cond_inner) == CLEANUP_POINT_EXPR) | |
3735 | cond_inner = TREE_OPERAND (cond_inner, 0); | |
3736 | location_t sloc = EXPR_LOCATION (SWITCH_STMT_COND (sw_stmt)); | |
6ffbf87c | 3737 | tree new_s = cp_build_init_expr (sloc, newvar, |
3dbc7721 IS |
3738 | cond_inner); |
3739 | finish_expr_stmt (new_s); | |
3740 | SWITCH_STMT_COND (sw_stmt) = newvar; | |
3741 | /* Now add the switch statement with the condition re- | |
3742 | written to use the local var. */ | |
3743 | add_stmt (sw_stmt); | |
3744 | *stmt = pop_stmt_list (insert_list); | |
3745 | /* Process the expanded list. */ | |
3746 | res = cp_walk_tree (stmt, await_statement_walker, | |
3747 | d, NULL); | |
3748 | *do_subtree = 0; /* Done subtrees. */ | |
3749 | return res; | |
3750 | } | |
3751 | break; | |
0f66b848 IS |
3752 | case CO_RETURN_EXPR: |
3753 | { | |
3754 | /* Expand the co_return as per [stmt.return.coroutine] | |
3755 | - for co_return; | |
3756 | { p.return_void (); goto final_suspend; } | |
3757 | - for co_return [void expr]; | |
3758 | { expr; p.return_void(); goto final_suspend;} | |
3759 | - for co_return [non void expr]; | |
3760 | { p.return_value(expr); goto final_suspend; } */ | |
0f66b848 IS |
3761 | location_t loc = EXPR_LOCATION (expr); |
3762 | tree call = TREE_OPERAND (expr, 1); | |
3763 | expr = TREE_OPERAND (expr, 0); | |
3764 | tree ret_list = push_stmt_list (); | |
3765 | /* [stmt.return.coroutine], 2.2 | |
3766 | If expr is present and void, it is placed immediately before | |
3767 | the call for return_void; */ | |
0f66b848 | 3768 | if (expr && VOID_TYPE_P (TREE_TYPE (expr))) |
ab08859e | 3769 | finish_expr_stmt (expr); |
0f66b848 IS |
3770 | /* Insert p.return_{void,value(expr)}. */ |
3771 | finish_expr_stmt (call); | |
8406ed9a IS |
3772 | TREE_USED (awpts->fs_label) = 1; |
3773 | add_stmt (build_stmt (loc, GOTO_EXPR, awpts->fs_label)); | |
0f66b848 | 3774 | *stmt = pop_stmt_list (ret_list); |
ab08859e | 3775 | res = cp_walk_tree (stmt, await_statement_walker, d, NULL); |
0f66b848 IS |
3776 | /* Once this is complete, we will have processed subtrees. */ |
3777 | *do_subtree = 0; | |
ab08859e | 3778 | return res; |
0f66b848 | 3779 | } |
650beb11 IS |
3780 | break; |
3781 | case HANDLER: | |
3782 | { | |
3783 | /* [expr.await] An await-expression shall appear only in a | |
3784 | potentially-evaluated expression within the compound-statement | |
3785 | of a function-body outside of a handler. */ | |
3786 | tree *await_ptr; | |
3787 | hash_set<tree> visited; | |
3788 | if (!(cp_walk_tree (&HANDLER_BODY (expr), find_any_await, | |
3789 | &await_ptr, &visited))) | |
3790 | return NULL_TREE; /* All OK. */ | |
3791 | location_t loc = EXPR_LOCATION (*await_ptr); | |
3792 | error_at (loc, "await expressions are not permitted in handlers"); | |
3793 | return NULL_TREE; /* This is going to fail later anyway. */ | |
3794 | } | |
3795 | break; | |
3dbc7721 | 3796 | } |
0f66b848 | 3797 | else if (EXPR_P (expr)) |
49789fd0 | 3798 | { |
ab08859e IS |
3799 | hash_set<tree> visited; |
3800 | tree *await_ptr; | |
3801 | if (!(cp_walk_tree (stmt, find_any_await, &await_ptr, &visited))) | |
3802 | return NULL_TREE; /* Nothing special to do here. */ | |
3803 | ||
3804 | visited.empty (); | |
3805 | awpts->saw_awaits = 0; | |
3806 | hash_set<tree> truth_aoif_to_expand; | |
3807 | awpts->truth_aoif_to_expand = &truth_aoif_to_expand; | |
3808 | awpts->needs_truth_if_exp = false; | |
3809 | awpts->has_awaiter_init = false; | |
3dbc7721 IS |
3810 | if ((res = cp_walk_tree (stmt, analyze_expression_awaits, d, &visited))) |
3811 | return res; | |
49789fd0 | 3812 | *do_subtree = 0; /* Done subtrees. */ |
3dbc7721 IS |
3813 | if (!awpts->saw_awaits) |
3814 | return NULL_TREE; /* Nothing special to do here. */ | |
3815 | ||
0f66b848 IS |
3816 | if (awpts->needs_truth_if_exp) |
3817 | { | |
3818 | /* If a truth-and/or-if expression has an await expression in the | |
3819 | conditionally-taken branch, then it must be rewritten into a | |
3820 | regular conditional. */ | |
3821 | truth_if_transform xf = {stmt, NULL_TREE, &truth_aoif_to_expand}; | |
3822 | if ((res = cp_walk_tree (stmt, expand_one_truth_if, &xf, NULL))) | |
3823 | return res; | |
3824 | } | |
3825 | /* Process this statement, which contains at least one await expression | |
3826 | to 'promote' temporary values to a coroutine frame slot. */ | |
3827 | return maybe_promote_temps (stmt, d); | |
49789fd0 | 3828 | } |
0666767e | 3829 | /* Continue recursion, if needed. */ |
49789fd0 IS |
3830 | return res; |
3831 | } | |
3832 | ||
3833 | /* For figuring out what param usage we have. */ | |
3834 | ||
3835 | struct param_frame_data | |
3836 | { | |
3837 | tree *field_list; | |
3838 | hash_map<tree, param_info> *param_uses; | |
cd14f288 | 3839 | hash_set<tree *> *visited; |
49789fd0 IS |
3840 | location_t loc; |
3841 | bool param_seen; | |
3842 | }; | |
3843 | ||
70ee703c IS |
3844 | /* A tree walk callback that rewrites each parm use to the local variable |
3845 | that represents its copy in the frame. */ | |
0f66b848 | 3846 | |
49789fd0 | 3847 | static tree |
70ee703c | 3848 | rewrite_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d) |
49789fd0 IS |
3849 | { |
3850 | param_frame_data *data = (param_frame_data *) d; | |
3851 | ||
cd14f288 | 3852 | /* For lambda closure content, we have to look specifically. */ |
8861c807 | 3853 | if (VAR_P (*stmt) && DECL_HAS_VALUE_EXPR_P (*stmt)) |
cd14f288 IS |
3854 | { |
3855 | tree t = DECL_VALUE_EXPR (*stmt); | |
70ee703c | 3856 | return cp_walk_tree (&t, rewrite_param_uses, d, NULL); |
cd14f288 IS |
3857 | } |
3858 | ||
1a37d6b7 AA |
3859 | if (unevaluated_p (TREE_CODE (*stmt))) |
3860 | { | |
3861 | /* No odr-uses in unevaluated operands. */ | |
3862 | *do_subtree = 0; | |
3863 | return NULL_TREE; | |
3864 | } | |
3865 | ||
49789fd0 IS |
3866 | if (TREE_CODE (*stmt) != PARM_DECL) |
3867 | return NULL_TREE; | |
3868 | ||
cd14f288 IS |
3869 | /* If we already saw the containing expression, then we're done. */ |
3870 | if (data->visited->add (stmt)) | |
3871 | return NULL_TREE; | |
3872 | ||
49789fd0 IS |
3873 | bool existed; |
3874 | param_info &parm = data->param_uses->get_or_insert (*stmt, &existed); | |
3875 | gcc_checking_assert (existed); | |
3876 | ||
70ee703c IS |
3877 | *stmt = parm.copy_var; |
3878 | return NULL_TREE; | |
3879 | } | |
3880 | ||
3881 | /* Build up a set of info that determines how each param copy will be | |
3882 | handled. */ | |
3883 | ||
d5da5f00 IS |
3884 | static void |
3885 | analyze_fn_parms (tree orig, hash_map<tree, param_info> *param_uses) | |
70ee703c IS |
3886 | { |
3887 | if (!DECL_ARGUMENTS (orig)) | |
d5da5f00 | 3888 | return; |
70ee703c IS |
3889 | |
3890 | /* Build a hash map with an entry for each param. | |
3891 | The key is the param tree. | |
3892 | Then we have an entry for the frame field name. | |
3893 | Then a cache for the field ref when we come to use it. | |
3894 | Then a tree list of the uses. | |
3895 | The second two entries start out empty - and only get populated | |
3896 | when we see uses. */ | |
3897 | bool lambda_p = LAMBDA_FUNCTION_P (orig); | |
3898 | ||
cbe1b1c6 IS |
3899 | /* Count the param copies from 1 as per the std. */ |
3900 | unsigned parm_num = 1; | |
3901 | for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; | |
3902 | ++parm_num, arg = DECL_CHAIN (arg)) | |
49789fd0 | 3903 | { |
70ee703c IS |
3904 | bool existed; |
3905 | param_info &parm = param_uses->get_or_insert (arg, &existed); | |
3906 | gcc_checking_assert (!existed); | |
3907 | parm.body_uses = NULL; | |
3908 | tree actual_type = TREE_TYPE (arg); | |
3909 | actual_type = complete_type_or_else (actual_type, orig); | |
3910 | if (actual_type == NULL_TREE) | |
3911 | actual_type = error_mark_node; | |
3912 | parm.orig_type = actual_type; | |
3913 | parm.by_ref = parm.pt_ref = parm.rv_ref = false; | |
3914 | if (TREE_CODE (actual_type) == REFERENCE_TYPE) | |
3915 | { | |
3916 | /* If the user passes by reference, then we will save the | |
3917 | pointer to the original. As noted in | |
3918 | [dcl.fct.def.coroutine] / 13, if the lifetime of the | |
3919 | referenced item ends and then the coroutine is resumed, | |
3920 | we have UB; well, the user asked for it. */ | |
3921 | if (TYPE_REF_IS_RVALUE (actual_type)) | |
3922 | parm.rv_ref = true; | |
3923 | else | |
3924 | parm.pt_ref = true; | |
3925 | } | |
3926 | else if (TYPE_REF_P (DECL_ARG_TYPE (arg))) | |
3927 | parm.by_ref = true; | |
3928 | ||
3929 | parm.frame_type = actual_type; | |
3930 | ||
3931 | parm.this_ptr = is_this_parameter (arg); | |
3932 | parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier; | |
3933 | ||
3934 | tree name = DECL_NAME (arg); | |
3935 | if (!name) | |
3936 | { | |
cbe1b1c6 | 3937 | char *buf = xasprintf ("_Coro_q%u___unnamed", parm_num); |
70ee703c IS |
3938 | name = get_identifier (buf); |
3939 | free (buf); | |
3940 | } | |
3941 | parm.field_id = name; | |
70ee703c IS |
3942 | if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type)) |
3943 | { | |
cbe1b1c6 IS |
3944 | char *buf = xasprintf ("_Coro_q%u_%s_live", parm_num, |
3945 | DECL_NAME (arg) ? IDENTIFIER_POINTER (name) | |
3946 | : "__unnamed"); | |
fae62716 IS |
3947 | parm.guard_var |
3948 | = coro_build_artificial_var (UNKNOWN_LOCATION, get_identifier (buf), | |
3949 | boolean_type_node, orig, | |
3950 | boolean_false_node); | |
70ee703c | 3951 | free (buf); |
70ee703c IS |
3952 | parm.trivial_dtor = false; |
3953 | } | |
3954 | else | |
3955 | parm.trivial_dtor = true; | |
49789fd0 | 3956 | } |
49789fd0 IS |
3957 | } |
3958 | ||
0f66b848 IS |
3959 | /* Small helper for the repetitive task of adding a new field to the coro |
3960 | frame type. */ | |
3961 | ||
3962 | static tree | |
3963 | coro_make_frame_entry (tree *field_list, const char *name, tree fld_type, | |
3964 | location_t loc) | |
3965 | { | |
3966 | tree id = get_identifier (name); | |
3967 | tree decl = build_decl (loc, FIELD_DECL, id, fld_type); | |
3968 | DECL_CHAIN (decl) = *field_list; | |
3969 | *field_list = decl; | |
3970 | return id; | |
3971 | } | |
3972 | ||
0f66b848 IS |
3973 | /* A tree-walk callback that processes one bind expression noting local |
3974 | variables, and making a coroutine frame slot available for those that | |
3975 | need it, so that they can be 'promoted' across suspension points. */ | |
3976 | ||
49789fd0 IS |
3977 | static tree |
3978 | register_local_var_uses (tree *stmt, int *do_subtree, void *d) | |
3979 | { | |
3980 | local_vars_frame_data *lvd = (local_vars_frame_data *) d; | |
3981 | ||
3982 | /* As we enter a bind expression - record the vars there and then recurse. | |
3983 | As we exit drop the nest depth. | |
3984 | The bind index is a growing count of how many bind indices we've seen. | |
80222642 IS |
3985 | We build a space in the frame for each local var. */ |
3986 | ||
49789fd0 IS |
3987 | if (TREE_CODE (*stmt) == BIND_EXPR) |
3988 | { | |
49789fd0 | 3989 | tree lvar; |
3d8d093e | 3990 | unsigned serial = 0; |
49789fd0 IS |
3991 | for (lvar = BIND_EXPR_VARS (*stmt); lvar != NULL; |
3992 | lvar = DECL_CHAIN (lvar)) | |
3993 | { | |
3994 | bool existed; | |
3995 | local_var_info &local_var | |
3996 | = lvd->local_var_uses->get_or_insert (lvar, &existed); | |
3997 | gcc_checking_assert (!existed); | |
cd14f288 | 3998 | local_var.def_loc = DECL_SOURCE_LOCATION (lvar); |
49789fd0 | 3999 | tree lvtype = TREE_TYPE (lvar); |
cd14f288 IS |
4000 | local_var.frame_type = lvtype; |
4001 | local_var.field_idx = local_var.field_id = NULL_TREE; | |
174b6f73 IS |
4002 | |
4003 | /* Make sure that we only present vars to the tests below. */ | |
a362c9ca AA |
4004 | if (TREE_CODE (lvar) != PARM_DECL |
4005 | && TREE_CODE (lvar) != VAR_DECL) | |
174b6f73 IS |
4006 | continue; |
4007 | ||
4008 | /* We don't move static vars into the frame. */ | |
4009 | local_var.is_static = TREE_STATIC (lvar); | |
4010 | if (local_var.is_static) | |
4011 | continue; | |
4012 | ||
fdf0b6ce IS |
4013 | poly_uint64 size; |
4014 | if (TREE_CODE (lvtype) == ARRAY_TYPE | |
4015 | && !poly_int_tree_p (DECL_SIZE_UNIT (lvar), &size)) | |
4016 | { | |
4017 | sorry_at (local_var.def_loc, "variable length arrays are not" | |
4018 | " yet supported in coroutines"); | |
4019 | /* Ignore it, this is broken anyway. */ | |
4020 | continue; | |
4021 | } | |
4022 | ||
cd14f288 IS |
4023 | lvd->local_var_seen = true; |
4024 | /* If this var is a lambda capture proxy, we want to leave it alone, | |
4025 | and later rewrite the DECL_VALUE_EXPR to indirect through the | |
4026 | frame copy of the pointer to the lambda closure object. */ | |
4027 | local_var.is_lambda_capture = is_capture_proxy (lvar); | |
4028 | if (local_var.is_lambda_capture) | |
4029 | continue; | |
4030 | ||
174b6f73 IS |
4031 | /* If a variable has a value expression, then that's what needs |
4032 | to be processed. */ | |
4033 | local_var.has_value_expr_p = DECL_HAS_VALUE_EXPR_P (lvar); | |
4034 | if (local_var.has_value_expr_p) | |
4035 | continue; | |
4036 | ||
49789fd0 | 4037 | /* Make names depth+index unique, so that we can support nested |
addf167a IS |
4038 | scopes with identically named locals and still be able to |
4039 | identify them in the coroutine frame. */ | |
cd14f288 | 4040 | tree lvname = DECL_NAME (lvar); |
15a176a8 | 4041 | char *buf = NULL; |
c5a735fa | 4042 | |
addf167a IS |
4043 | /* The outermost bind scope contains the artificial variables that |
4044 | we inject to implement the coro state machine. We want to be able | |
4045 | to inspect these in debugging. */ | |
4046 | if (lvname != NULL_TREE && lvd->nest_depth == 0) | |
4047 | buf = xasprintf ("%s", IDENTIFIER_POINTER (lvname)); | |
4048 | else if (lvname != NULL_TREE) | |
4049 | buf = xasprintf ("%s_%u_%u", IDENTIFIER_POINTER (lvname), | |
0f66b848 | 4050 | lvd->nest_depth, lvd->bind_indx); |
3d8d093e IS |
4051 | else |
4052 | buf = xasprintf ("_D%u_%u_%u", lvd->nest_depth, lvd->bind_indx, | |
4053 | serial++); | |
4054 | ||
49789fd0 IS |
4055 | /* TODO: Figure out if we should build a local type that has any |
4056 | excess alignment or size from the original decl. */ | |
3d8d093e IS |
4057 | local_var.field_id = coro_make_frame_entry (lvd->field_list, buf, |
4058 | lvtype, lvd->loc); | |
4059 | free (buf); | |
49789fd0 IS |
4060 | /* We don't walk any of the local var sub-trees, they won't contain |
4061 | any bind exprs. */ | |
4062 | } | |
addf167a IS |
4063 | lvd->bind_indx++; |
4064 | lvd->nest_depth++; | |
49789fd0 IS |
4065 | cp_walk_tree (&BIND_EXPR_BODY (*stmt), register_local_var_uses, d, NULL); |
4066 | *do_subtree = 0; /* We've done this. */ | |
4067 | lvd->nest_depth--; | |
4068 | } | |
4069 | return NULL_TREE; | |
4070 | } | |
4071 | ||
237ab3ee IS |
4072 | /* Build, return FUNCTION_DECL node based on ORIG with a type FN_TYPE which has |
4073 | a single argument of type CORO_FRAME_PTR. Build the actor function if | |
4074 | ACTOR_P is true, otherwise the destroy. */ | |
3904cc10 BC |
4075 | |
4076 | static tree | |
237ab3ee IS |
4077 | coro_build_actor_or_destroy_function (tree orig, tree fn_type, |
4078 | tree coro_frame_ptr, bool actor_p) | |
3904cc10 | 4079 | { |
d5b1bb0d | 4080 | location_t loc = DECL_SOURCE_LOCATION (orig); |
237ab3ee IS |
4081 | tree fn |
4082 | = build_lang_decl (FUNCTION_DECL, copy_node (DECL_NAME (orig)), fn_type); | |
4083 | ||
4084 | /* Allow for locating the ramp (original) function from this one. */ | |
4085 | if (!to_ramp) | |
4086 | to_ramp = hash_map<tree, tree>::create_ggc (10); | |
4087 | to_ramp->put (fn, orig); | |
4088 | ||
3904cc10 | 4089 | DECL_CONTEXT (fn) = DECL_CONTEXT (orig); |
d5b1bb0d | 4090 | DECL_SOURCE_LOCATION (fn) = loc; |
1e5da6a0 | 4091 | DECL_ARTIFICIAL (fn) = true; |
3904cc10 | 4092 | DECL_INITIAL (fn) = error_mark_node; |
d5b1bb0d | 4093 | |
3904cc10 BC |
4094 | tree id = get_identifier ("frame_ptr"); |
4095 | tree fp = build_lang_decl (PARM_DECL, id, coro_frame_ptr); | |
8d6d6c86 | 4096 | DECL_ARTIFICIAL (fp) = true; |
3904cc10 BC |
4097 | DECL_CONTEXT (fp) = fn; |
4098 | DECL_ARG_TYPE (fp) = type_passed_as (coro_frame_ptr); | |
4099 | DECL_ARGUMENTS (fn) = fp; | |
d5b1bb0d | 4100 | |
1e5da6a0 IS |
4101 | /* Copy selected attributes from the original function. */ |
4102 | TREE_USED (fn) = TREE_USED (orig); | |
4103 | if (DECL_SECTION_NAME (orig)) | |
8b9a92f7 | 4104 | set_decl_section_name (fn, orig); |
1e5da6a0 IS |
4105 | /* Copy any alignment that the FE added. */ |
4106 | if (DECL_ALIGN (orig)) | |
4107 | SET_DECL_ALIGN (fn, DECL_ALIGN (orig)); | |
4108 | /* Copy any alignment the user added. */ | |
4109 | DECL_USER_ALIGN (fn) = DECL_USER_ALIGN (orig); | |
4110 | /* Apply attributes from the original fn. */ | |
4111 | DECL_ATTRIBUTES (fn) = copy_list (DECL_ATTRIBUTES (orig)); | |
d1706235 IS |
4112 | /* but we do not want ones for contracts. */ |
4113 | remove_contract_attributes (fn); | |
d5b1bb0d IS |
4114 | |
4115 | /* A void return. */ | |
4116 | tree resdecl = build_decl (loc, RESULT_DECL, 0, void_type_node); | |
4117 | DECL_CONTEXT (resdecl) = fn; | |
4118 | DECL_ARTIFICIAL (resdecl) = 1; | |
4119 | DECL_IGNORED_P (resdecl) = 1; | |
4120 | DECL_RESULT (fn) = resdecl; | |
4121 | ||
237ab3ee IS |
4122 | /* Set up a means to find out if a decl is one of the helpers and, if so, |
4123 | which one. */ | |
4124 | if (coroutine_info *info = get_coroutine_info (orig)) | |
4125 | { | |
4126 | gcc_checking_assert ((actor_p && info->actor_decl == NULL_TREE) | |
4127 | || info->destroy_decl == NULL_TREE); | |
4128 | if (actor_p) | |
4129 | info->actor_decl = fn; | |
4130 | else | |
4131 | info->destroy_decl = fn; | |
4132 | } | |
3904cc10 BC |
4133 | return fn; |
4134 | } | |
4135 | ||
9252a208 IS |
4136 | /* Re-write the body as per [dcl.fct.def.coroutine] / 5. */ |
4137 | ||
6303cd7e IS |
4138 | void |
4139 | cp_coroutine_transform::wrap_original_function_body () | |
3dbc7721 | 4140 | { |
6303cd7e IS |
4141 | /* Avoid the code here attaching a location that makes the debugger jump. */ |
4142 | iloc_sentinel stable_input_loc (fn_start); | |
4143 | location_t loc = UNKNOWN_LOCATION; | |
4144 | input_location = loc; | |
4145 | ||
9252a208 | 4146 | /* This will be our new outer scope. */ |
6303cd7e IS |
4147 | tree update_body |
4148 | = build3_loc (loc, BIND_EXPR, void_type_node, NULL, NULL, NULL); | |
9252a208 IS |
4149 | tree top_block = make_node (BLOCK); |
4150 | BIND_EXPR_BLOCK (update_body) = top_block; | |
4151 | BIND_EXPR_BODY (update_body) = push_stmt_list (); | |
3dbc7721 | 4152 | |
9252a208 IS |
4153 | /* If the function has a top level bind expression, then connect that |
4154 | after first making sure we give it a new block. */ | |
6303cd7e | 4155 | tree first = expr_first (coroutine_body); |
9252a208 IS |
4156 | if (first && TREE_CODE (first) == BIND_EXPR) |
4157 | { | |
4158 | tree block = BIND_EXPR_BLOCK (first); | |
4159 | gcc_checking_assert (block); | |
4160 | gcc_checking_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE); | |
4161 | gcc_checking_assert (BLOCK_CHAIN (block) == NULL_TREE); | |
4162 | /* Replace the top block to avoid issues with locations for args | |
4163 | appearing to be in a non-existent place. */ | |
4164 | tree replace_blk = make_node (BLOCK); | |
4165 | BLOCK_VARS (replace_blk) = BLOCK_VARS (block); | |
4166 | BLOCK_SUBBLOCKS (replace_blk) = BLOCK_SUBBLOCKS (block); | |
4167 | for (tree b = BLOCK_SUBBLOCKS (replace_blk); b; b = BLOCK_CHAIN (b)) | |
4168 | BLOCK_SUPERCONTEXT (b) = replace_blk; | |
4169 | BIND_EXPR_BLOCK (first) = replace_blk; | |
4170 | /* The top block has one child, so far, and we have now got a | |
4171 | superblock. */ | |
0d5db79a IS |
4172 | BLOCK_SUPERCONTEXT (replace_blk) = top_block; |
4173 | BLOCK_SUBBLOCKS (top_block) = replace_blk; | |
9252a208 | 4174 | } |
a961ad1b AA |
4175 | else |
4176 | { | |
4177 | /* We are missing a top level BIND_EXPR. We need one to ensure that we | |
4178 | don't shuffle around the coroutine frame and corrupt it. */ | |
6303cd7e | 4179 | tree bind_wrap = build3_loc (loc, BIND_EXPR, void_type_node, |
a961ad1b | 4180 | NULL, NULL, NULL); |
6303cd7e | 4181 | BIND_EXPR_BODY (bind_wrap) = coroutine_body; |
a8d7631d IS |
4182 | /* Ensure we have a block to connect up the scopes. */ |
4183 | tree new_blk = make_node (BLOCK); | |
4184 | BIND_EXPR_BLOCK (bind_wrap) = new_blk; | |
4185 | BLOCK_SUBBLOCKS (top_block) = new_blk; | |
6303cd7e | 4186 | coroutine_body = bind_wrap; |
a961ad1b | 4187 | } |
3dbc7721 | 4188 | |
9252a208 IS |
4189 | /* Wrap the function body in a try {} catch (...) {} block, if exceptions |
4190 | are enabled. */ | |
9252a208 IS |
4191 | tree var_list = NULL_TREE; |
4192 | tree initial_await = build_init_or_final_await (fn_start, false); | |
4193 | ||
4194 | /* [stmt.return.coroutine] / 3 | |
4195 | If p.return_void() is a valid expression, flowing off the end of a | |
4196 | coroutine is equivalent to a co_return with no operand; otherwise | |
4197 | flowing off the end of a coroutine results in undefined behavior. */ | |
4198 | tree return_void | |
6303cd7e | 4199 | = get_coroutine_return_void_expr (orig_fn_decl, loc, false); |
9252a208 | 4200 | |
c5a735fa IS |
4201 | /* The pointer to the resume function. */ |
4202 | tree resume_fn_ptr | |
6303cd7e IS |
4203 | = coro_build_artificial_var (loc, coro_resume_fn_id, |
4204 | act_des_fn_ptr_type, orig_fn_decl, NULL_TREE); | |
c5a735fa IS |
4205 | DECL_CHAIN (resume_fn_ptr) = var_list; |
4206 | var_list = resume_fn_ptr; | |
4207 | add_decl_expr (resume_fn_ptr); | |
4208 | ||
020b286c IS |
4209 | /* We will need to be able to set the resume function pointer to nullptr |
4210 | to signal that the coroutine is 'done'. */ | |
020b286c | 4211 | tree zero_resume |
6303cd7e | 4212 | = build1 (CONVERT_EXPR, act_des_fn_ptr_type, nullptr_node); |
c5a735fa IS |
4213 | |
4214 | /* The pointer to the destroy function. */ | |
6303cd7e IS |
4215 | tree var |
4216 | = coro_build_artificial_var (loc, coro_destroy_fn_id, | |
4217 | act_des_fn_ptr_type, orig_fn_decl, NULL_TREE); | |
c5a735fa IS |
4218 | DECL_CHAIN (var) = var_list; |
4219 | var_list = var; | |
4220 | add_decl_expr (var); | |
4221 | ||
4222 | /* The promise was created on demand when parsing we now link it into | |
4223 | our scope. */ | |
6303cd7e IS |
4224 | tree promise = get_coroutine_promise_proxy (orig_fn_decl); |
4225 | DECL_CONTEXT (promise) = orig_fn_decl; | |
4226 | DECL_SOURCE_LOCATION (promise) = loc; | |
c5a735fa IS |
4227 | DECL_CHAIN (promise) = var_list; |
4228 | var_list = promise; | |
4229 | add_decl_expr (promise); | |
4230 | ||
4231 | /* We need a handle to this coroutine, which is passed to every | |
4232 | await_suspend(). This was created on demand when parsing we now link it | |
4233 | into our scope. */ | |
6303cd7e IS |
4234 | var = get_coroutine_self_handle_proxy (orig_fn_decl); |
4235 | DECL_CONTEXT (var) = orig_fn_decl; | |
4236 | DECL_SOURCE_LOCATION (var) = loc; | |
c5a735fa IS |
4237 | DECL_CHAIN (var) = var_list; |
4238 | var_list = var; | |
4239 | add_decl_expr (var); | |
4240 | ||
70ee703c | 4241 | /* If we have function parms, then these will be copied to the coroutine |
d5da5f00 IS |
4242 | frame as per [dcl.fct.def.coroutine] / 13. |
4243 | Here, we create a local (proxy) variable for each parm, since the original | |
4244 | parms will be out of scope once the ramp has finished. The proxy vars will | |
70ee703c IS |
4245 | get DECL_VALUE_EXPRs pointing to the frame copies, so that we can interact |
4246 | with them in the debugger. */ | |
6303cd7e | 4247 | if (DECL_ARGUMENTS (orig_fn_decl)) |
70ee703c | 4248 | { |
70ee703c | 4249 | /* Add a local var for each parm. */ |
6303cd7e | 4250 | for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; |
70ee703c IS |
4251 | arg = DECL_CHAIN (arg)) |
4252 | { | |
6303cd7e | 4253 | param_info *parm_i = param_uses.get (arg); |
70ee703c IS |
4254 | gcc_checking_assert (parm_i); |
4255 | parm_i->copy_var | |
4256 | = build_lang_decl (VAR_DECL, parm_i->field_id, TREE_TYPE (arg)); | |
4257 | DECL_SOURCE_LOCATION (parm_i->copy_var) = DECL_SOURCE_LOCATION (arg); | |
6303cd7e | 4258 | DECL_CONTEXT (parm_i->copy_var) = orig_fn_decl; |
70ee703c IS |
4259 | DECL_ARTIFICIAL (parm_i->copy_var) = true; |
4260 | DECL_CHAIN (parm_i->copy_var) = var_list; | |
4261 | var_list = parm_i->copy_var; | |
4262 | add_decl_expr (parm_i->copy_var); | |
d5da5f00 | 4263 | } |
70ee703c IS |
4264 | |
4265 | /* Now replace all uses of the parms in the function body with the proxy | |
4266 | vars. We want to this to apply to every instance of param's use, so | |
4267 | don't include a 'visited' hash_set on the tree walk, however we will | |
4268 | arrange to visit each containing expression only once. */ | |
4269 | hash_set<tree *> visited; | |
6303cd7e IS |
4270 | param_frame_data param_data = {NULL, ¶m_uses, |
4271 | &visited, loc, false}; | |
4272 | cp_walk_tree (&coroutine_body, rewrite_param_uses, ¶m_data, NULL); | |
70ee703c | 4273 | } |
c5a735fa IS |
4274 | |
4275 | /* We create a resume index, this is initialized in the ramp. */ | |
4276 | resume_idx_var | |
6303cd7e IS |
4277 | = coro_build_artificial_var (loc, coro_resume_index_id, |
4278 | short_unsigned_type_node, orig_fn_decl, | |
4279 | NULL_TREE); | |
c5a735fa IS |
4280 | DECL_CHAIN (resume_idx_var) = var_list; |
4281 | var_list = resume_idx_var; | |
4282 | add_decl_expr (resume_idx_var); | |
4283 | ||
4284 | /* If the coroutine has a frame that needs to be freed, this will be set by | |
4285 | the ramp. */ | |
4286 | var = coro_build_artificial_var (fn_start, coro_frame_needs_free_id, | |
6303cd7e | 4287 | boolean_type_node, orig_fn_decl, NULL_TREE); |
c5a735fa IS |
4288 | DECL_CHAIN (var) = var_list; |
4289 | var_list = var; | |
4290 | add_decl_expr (var); | |
020b286c | 4291 | |
9252a208 IS |
4292 | if (flag_exceptions) |
4293 | { | |
4294 | /* Build promise.unhandled_exception(); */ | |
4295 | tree ueh | |
6303cd7e | 4296 | = coro_build_promise_expression (orig_fn_decl, promise, |
9252a208 IS |
4297 | coro_unhandled_exception_identifier, |
4298 | fn_start, NULL, /*musthave=*/true); | |
4299 | /* Create and initialize the initial-await-resume-called variable per | |
4300 | [dcl.fct.def.coroutine] / 5.3. */ | |
a45a7ecd | 4301 | tree i_a_r_c |
6303cd7e IS |
4302 | = coro_build_artificial_var (loc, coro_frame_i_a_r_c_id, |
4303 | boolean_type_node, orig_fn_decl, | |
a45a7ecd | 4304 | boolean_false_node); |
9252a208 IS |
4305 | DECL_CHAIN (i_a_r_c) = var_list; |
4306 | var_list = i_a_r_c; | |
9252a208 IS |
4307 | add_decl_expr (i_a_r_c); |
4308 | /* Start the try-catch. */ | |
6303cd7e | 4309 | tree tcb = build_stmt (loc, TRY_BLOCK, NULL_TREE, NULL_TREE); |
9252a208 IS |
4310 | add_stmt (tcb); |
4311 | TRY_STMTS (tcb) = push_stmt_list (); | |
4312 | if (initial_await != error_mark_node) | |
4313 | { | |
4314 | /* Build a compound expression that sets the | |
4315 | initial-await-resume-called variable true and then calls the | |
2466a8d0 IS |
4316 | initial suspend expression await resume. |
4317 | In the case that the user decides to make the initial await | |
4318 | await_resume() return a value, we need to discard it and, it is | |
4319 | a reference type, look past the indirection. */ | |
4320 | if (INDIRECT_REF_P (initial_await)) | |
4321 | initial_await = TREE_OPERAND (initial_await, 0); | |
c442a9b7 IS |
4322 | /* In the case that the initial_await returns a target expression |
4323 | we might need to look through that to update the await expr. */ | |
4324 | tree iaw = initial_await; | |
4325 | if (TREE_CODE (iaw) == TARGET_EXPR) | |
4326 | iaw = TARGET_EXPR_INITIAL (iaw); | |
4327 | gcc_checking_assert (TREE_CODE (iaw) == CO_AWAIT_EXPR); | |
4328 | tree vec = TREE_OPERAND (iaw, 3); | |
9252a208 | 4329 | tree aw_r = TREE_VEC_ELT (vec, 2); |
2466a8d0 | 4330 | aw_r = convert_to_void (aw_r, ICV_STATEMENT, tf_warning_or_error); |
9252a208 IS |
4331 | tree update = build2 (MODIFY_EXPR, boolean_type_node, i_a_r_c, |
4332 | boolean_true_node); | |
4333 | aw_r = cp_build_compound_expr (update, aw_r, tf_warning_or_error); | |
4334 | TREE_VEC_ELT (vec, 2) = aw_r; | |
4335 | } | |
4336 | /* Add the initial await to the start of the user-authored function. */ | |
4337 | finish_expr_stmt (initial_await); | |
4338 | /* Append the original function body. */ | |
6303cd7e | 4339 | add_stmt (coroutine_body); |
9252a208 IS |
4340 | if (return_void) |
4341 | add_stmt (return_void); | |
4342 | TRY_STMTS (tcb) = pop_stmt_list (TRY_STMTS (tcb)); | |
4343 | TRY_HANDLERS (tcb) = push_stmt_list (); | |
4344 | /* Mimic what the parser does for the catch. */ | |
4345 | tree handler = begin_handler (); | |
4346 | finish_handler_parms (NULL_TREE, handler); /* catch (...) */ | |
4347 | ||
4348 | /* Get the initial await resume called value. */ | |
4349 | tree not_iarc_if = begin_if_stmt (); | |
6303cd7e | 4350 | tree not_iarc = build1_loc (loc, TRUTH_NOT_EXPR, |
9252a208 IS |
4351 | boolean_type_node, i_a_r_c); |
4352 | finish_if_stmt_cond (not_iarc, not_iarc_if); | |
4353 | /* If the initial await resume called value is false, rethrow... */ | |
6303cd7e | 4354 | tree rethrow = build_throw (loc, NULL_TREE, tf_warning_or_error); |
65870e75 | 4355 | suppress_warning (rethrow); |
9252a208 IS |
4356 | finish_expr_stmt (rethrow); |
4357 | finish_then_clause (not_iarc_if); | |
4358 | tree iarc_scope = IF_SCOPE (not_iarc_if); | |
4359 | IF_SCOPE (not_iarc_if) = NULL; | |
4360 | not_iarc_if = do_poplevel (iarc_scope); | |
4361 | add_stmt (not_iarc_if); | |
020b286c IS |
4362 | /* ... else call the promise unhandled exception method |
4363 | but first we set done = true and the resume index to 0. | |
4364 | If the unhandled exception method returns, then we continue | |
4365 | to the final await expression (which duplicates the clearing of | |
4366 | the field). */ | |
6303cd7e IS |
4367 | tree r = build2_loc (loc, MODIFY_EXPR, act_des_fn_ptr_type, resume_fn_ptr, |
4368 | zero_resume); | |
c5a735fa IS |
4369 | finish_expr_stmt (r); |
4370 | tree short_zero = build_int_cst (short_unsigned_type_node, 0); | |
4371 | r = build2 (MODIFY_EXPR, short_unsigned_type_node, resume_idx_var, | |
4372 | short_zero); | |
4373 | finish_expr_stmt (r); | |
4374 | finish_expr_stmt (ueh); | |
9252a208 IS |
4375 | finish_handler (handler); |
4376 | TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb)); | |
4377 | } | |
4378 | else | |
4379 | { | |
4380 | if (pedantic) | |
4381 | { | |
4382 | /* We still try to look for the promise method and warn if it's not | |
4383 | present. */ | |
4384 | tree ueh_meth | |
6303cd7e IS |
4385 | = lookup_promise_method (orig_fn_decl, |
4386 | coro_unhandled_exception_identifier, | |
9252a208 IS |
4387 | fn_start, /*musthave=*/false); |
4388 | if (!ueh_meth || ueh_meth == error_mark_node) | |
4389 | warning_at (fn_start, 0, "no member named %qE in %qT", | |
4390 | coro_unhandled_exception_identifier, | |
6303cd7e | 4391 | get_coroutine_promise_type (orig_fn_decl)); |
9252a208 IS |
4392 | } |
4393 | /* Else we don't check and don't care if the method is missing.. | |
4394 | just add the initial suspend, function and return. */ | |
4395 | finish_expr_stmt (initial_await); | |
4396 | /* Append the original function body. */ | |
6303cd7e | 4397 | add_stmt (coroutine_body); |
9252a208 IS |
4398 | if (return_void) |
4399 | add_stmt (return_void); | |
4400 | } | |
4401 | ||
4402 | /* co_return branches to the final_suspend label, so declare that now. */ | |
4403 | fs_label | |
6303cd7e IS |
4404 | = create_named_label_with_ctx (loc, "final.suspend", NULL_TREE); |
4405 | add_stmt (build_stmt (loc, LABEL_EXPR, fs_label)); | |
9252a208 IS |
4406 | |
4407 | /* Before entering the final suspend point, we signal that this point has | |
4408 | been reached by setting the resume function pointer to zero (this is | |
4409 | what the 'done()' builtin tests) as per the current ABI. */ | |
6303cd7e IS |
4410 | zero_resume = build2_loc (loc, MODIFY_EXPR, act_des_fn_ptr_type, |
4411 | resume_fn_ptr, zero_resume); | |
9252a208 IS |
4412 | finish_expr_stmt (zero_resume); |
4413 | finish_expr_stmt (build_init_or_final_await (fn_start, true)); | |
4414 | BIND_EXPR_BODY (update_body) = pop_stmt_list (BIND_EXPR_BODY (update_body)); | |
4415 | BIND_EXPR_VARS (update_body) = nreverse (var_list); | |
4416 | BLOCK_VARS (top_block) = BIND_EXPR_VARS (update_body); | |
4417 | ||
6303cd7e | 4418 | coroutine_body = update_body; |
3dbc7721 | 4419 | } |
3dbc7721 | 4420 | |
86512292 IS |
4421 | /* Extract the body of the function we are going to outline, leaving |
4422 | to original function decl ready to build the ramp. */ | |
4423 | ||
4424 | static tree | |
4425 | split_coroutine_body_from_ramp (tree fndecl) | |
4426 | { | |
3949b7c0 IS |
4427 | /* Sanity-check and punt if we have a nonsense tree because of earlier |
4428 | parse errors, perhaps. */ | |
4429 | if (!current_binding_level | |
4430 | || current_binding_level->kind != sk_function_parms) | |
4431 | return NULL_TREE; | |
4432 | ||
86512292 IS |
4433 | /* Once we've tied off the original user-authored body in fn_body. |
4434 | Start the replacement synthesized ramp body. */ | |
4435 | ||
3949b7c0 | 4436 | tree body; |
86512292 IS |
4437 | if (use_eh_spec_block (fndecl)) |
4438 | { | |
4439 | body = pop_stmt_list (TREE_OPERAND (current_eh_spec_block, 0)); | |
4440 | TREE_OPERAND (current_eh_spec_block, 0) = push_stmt_list (); | |
4441 | } | |
4442 | else | |
4443 | { | |
4444 | body = pop_stmt_list (DECL_SAVED_TREE (fndecl)); | |
4445 | DECL_SAVED_TREE (fndecl) = push_stmt_list (); | |
4446 | } | |
4447 | ||
4448 | /* We can't validly get here with an empty statement list, since there's no | |
4449 | way for the FE to decide it's a coroutine in the absence of any code. */ | |
4450 | gcc_checking_assert (body != NULL_TREE); | |
4451 | ||
4452 | /* If we have an empty or erroneous function body, do not try to transform it | |
4453 | since that would potentially wrap errors. */ | |
4454 | tree body_start = expr_first (body); | |
4455 | if (body_start == NULL_TREE || body_start == error_mark_node) | |
4456 | { | |
4457 | /* Restore the original state. */ | |
4458 | add_stmt (body); | |
4459 | return NULL_TREE; | |
4460 | } | |
4461 | return body; | |
4462 | } | |
4463 | ||
624fb5b4 IS |
4464 | /* Build the expression to allocate the coroutine frame according to the |
4465 | rules of [dcl.fct.def.coroutine] / 9. */ | |
49789fd0 | 4466 | |
624fb5b4 IS |
4467 | static tree |
4468 | build_coroutine_frame_alloc_expr (tree promise_type, tree orig_fn_decl, | |
4469 | location_t fn_start, tree grooaf, | |
4470 | hash_map<tree, param_info> *param_uses, | |
4471 | tree frame_size) | |
49789fd0 | 4472 | { |
68bb7e3b | 4473 | /* Allocate the frame, this has several possibilities: |
80644a67 | 4474 | [dcl.fct.def.coroutine] / 9 (part 1) |
68bb7e3b | 4475 | The allocation function’s name is looked up in the scope of the promise |
624fb5b4 | 4476 | type. It is not a failure for it to be absent see part 4, below. */ |
445d8da5 | 4477 | |
49789fd0 | 4478 | tree nwname = ovl_op_identifier (false, NEW_EXPR); |
624fb5b4 IS |
4479 | tree new_fn_call = NULL_TREE; |
4480 | tree dummy_promise | |
4481 | = build_dummy_object (get_coroutine_promise_type (orig_fn_decl)); | |
445d8da5 IS |
4482 | |
4483 | if (TYPE_HAS_NEW_OPERATOR (promise_type)) | |
68bb7e3b | 4484 | { |
6303cd7e | 4485 | tree fns = lookup_promise_method (orig_fn_decl, nwname, fn_start, |
e74c7607 | 4486 | /*musthave=*/true); |
80644a67 | 4487 | /* [dcl.fct.def.coroutine] / 9 (part 2) |
68bb7e3b IS |
4488 | If the lookup finds an allocation function in the scope of the promise |
4489 | type, overload resolution is performed on a function call created by | |
4490 | assembling an argument list. The first argument is the amount of space | |
921942a8 IS |
4491 | requested, and has type std::size_t. The lvalues p1...pn are the |
4492 | succeeding arguments.. */ | |
68bb7e3b | 4493 | vec<tree, va_gc> *args = make_tree_vector (); |
624fb5b4 | 4494 | vec_safe_push (args, frame_size); /* Space needed. */ |
f5edc76a | 4495 | |
6303cd7e | 4496 | for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; |
68bb7e3b | 4497 | arg = DECL_CHAIN (arg)) |
f5edc76a | 4498 | { |
624fb5b4 | 4499 | param_info *parm_i = param_uses->get (arg); |
f5edc76a | 4500 | gcc_checking_assert (parm_i); |
1c140cfb | 4501 | if (parm_i->this_ptr || parm_i->lambda_cobj) |
f5edc76a IS |
4502 | { |
4503 | /* We pass a reference to *this to the allocator lookup. */ | |
303bed67 PP |
4504 | /* It's unsafe to use the cp_ version here since current_class_ref |
4505 | might've gotten clobbered earlier during rewrite_param_uses. */ | |
4506 | tree this_ref = build_fold_indirect_ref (arg); | |
7c5a9bf1 | 4507 | vec_safe_push (args, this_ref); |
f5edc76a IS |
4508 | } |
4509 | else | |
921942a8 | 4510 | vec_safe_push (args, convert_from_reference (arg)); |
f5edc76a | 4511 | } |
49789fd0 | 4512 | |
445d8da5 | 4513 | /* Note the function selected; we test to see if it's NOTHROW. */ |
68bb7e3b | 4514 | tree func; |
445d8da5 | 4515 | /* Failure is not an error for this attempt. */ |
624fb5b4 | 4516 | new_fn_call = build_new_method_call (dummy_promise, fns, &args, NULL, |
68bb7e3b IS |
4517 | LOOKUP_NORMAL, &func, tf_none); |
4518 | release_tree_vector (args); | |
4519 | ||
624fb5b4 | 4520 | if (new_fn_call == error_mark_node) |
68bb7e3b | 4521 | { |
80644a67 | 4522 | /* [dcl.fct.def.coroutine] / 9 (part 3) |
68bb7e3b IS |
4523 | If no viable function is found, overload resolution is performed |
4524 | again on a function call created by passing just the amount of | |
4525 | space required as an argument of type std::size_t. */ | |
624fb5b4 IS |
4526 | args = make_tree_vector_single (frame_size); /* Space needed. */ |
4527 | new_fn_call = build_new_method_call (dummy_promise, fns, &args, | |
68bb7e3b IS |
4528 | NULL_TREE, LOOKUP_NORMAL, &func, |
4529 | tf_none); | |
4530 | release_tree_vector (args); | |
4531 | } | |
49789fd0 | 4532 | |
445d8da5 IS |
4533 | /* However, if the promise provides an operator new, then one of these |
4534 | two options must be available. */ | |
624fb5b4 | 4535 | if (new_fn_call == error_mark_node) |
68bb7e3b IS |
4536 | { |
4537 | error_at (fn_start, "%qE is provided by %qT but is not usable with" | |
6303cd7e IS |
4538 | " the function signature %qD", nwname, promise_type, |
4539 | orig_fn_decl); | |
624fb5b4 | 4540 | return error_mark_node; |
68bb7e3b IS |
4541 | } |
4542 | else if (grooaf && !TYPE_NOTHROW_P (TREE_TYPE (func))) | |
6303cd7e IS |
4543 | { |
4544 | error_at (fn_start, "%qE is provided by %qT but %qE is not marked" | |
68bb7e3b | 4545 | " %<throw()%> or %<noexcept%>", grooaf, promise_type, nwname); |
624fb5b4 | 4546 | return error_mark_node; |
6303cd7e | 4547 | } |
9c5ca11a IS |
4548 | else if (!grooaf && TYPE_NOTHROW_P (TREE_TYPE (func))) |
4549 | warning_at (fn_start, 0, "%qE is marked %<throw()%> or %<noexcept%> but" | |
4550 | " no usable %<get_return_object_on_allocation_failure%>" | |
eef63aac | 4551 | " is provided by %qT", nwname, promise_type); |
68bb7e3b | 4552 | } |
445d8da5 | 4553 | else /* No operator new in the promise. */ |
49789fd0 | 4554 | { |
80644a67 | 4555 | /* [dcl.fct.def.coroutine] / 9 (part 4) |
68bb7e3b IS |
4556 | If this lookup fails, the allocation function’s name is looked up in |
4557 | the global scope. */ | |
4558 | ||
4559 | vec<tree, va_gc> *args; | |
4560 | /* build_operator_new_call () will insert size needed as element 0 of | |
4561 | this, and we might need to append the std::nothrow constant. */ | |
4562 | vec_alloc (args, 2); | |
68bb7e3b IS |
4563 | if (grooaf) |
4564 | { | |
80644a67 | 4565 | /* [dcl.fct.def.coroutine] / 10 (part 2) |
68bb7e3b IS |
4566 | If any declarations (of the get return on allocation fail) are |
4567 | found, then the result of a call to an allocation function used | |
4568 | to obtain storage for the coroutine state is assumed to return | |
4569 | nullptr if it fails to obtain storage and, if a global allocation | |
4570 | function is selected, the ::operator new(size_t, nothrow_t) form | |
4571 | is used. The allocation function used in this case shall have a | |
4572 | non-throwing noexcept-specification. So we need std::nothrow. */ | |
4573 | tree std_nt = lookup_qualified_name (std_node, | |
4574 | get_identifier ("nothrow"), | |
4c58a32f | 4575 | LOOK_want::NORMAL, |
db1c2a89 | 4576 | /*complain=*/true); |
445d8da5 | 4577 | if (!std_nt || std_nt == error_mark_node) |
624fb5b4 IS |
4578 | { |
4579 | /* Something is seriously wrong, punt. */ | |
4580 | error_at (fn_start, "%qE is provided by %qT but %<std::nothrow%>" | |
4581 | " cannot be found", grooaf, promise_type); | |
4582 | return error_mark_node; | |
4583 | } | |
68bb7e3b IS |
4584 | vec_safe_push (args, std_nt); |
4585 | } | |
4586 | ||
4587 | /* If we get to this point, we must succeed in looking up the global | |
624fb5b4 IS |
4588 | operator new for the params provided. Since we are not setting |
4589 | size_check or cookie, we expect frame_size to be unaltered. */ | |
68bb7e3b | 4590 | tree cookie = NULL; |
624fb5b4 IS |
4591 | new_fn_call = build_operator_new_call (nwname, &args, &frame_size, |
4592 | &cookie, /*align_arg=*/NULL, | |
4593 | /*size_check=*/NULL, /*fn=*/NULL, | |
4594 | tf_warning_or_error); | |
68bb7e3b | 4595 | release_tree_vector (args); |
49789fd0 | 4596 | } |
624fb5b4 IS |
4597 | return new_fn_call; |
4598 | } | |
4599 | ||
4600 | /* Build an expression to delete the coroutine state frame. */ | |
4601 | ||
4602 | static tree | |
4603 | build_coroutine_frame_delete_expr (tree coro_fp, tree frame_size, | |
4604 | tree promise_type, location_t loc) | |
4605 | { | |
4606 | /* Cast the frame pointer to a pointer to promise so that the build op | |
4607 | delete call will search the promise. */ | |
4608 | tree pptr_type = build_pointer_type (promise_type); | |
4609 | tree frame_arg = build1_loc (loc, CONVERT_EXPR, pptr_type, coro_fp); | |
4610 | /* [dcl.fct.def.coroutine] / 12 sentence 3: | |
4611 | If both a usual deallocation function with only a pointer parameter and | |
4612 | a usual deallocation function with both a pointer parameter and a size | |
4613 | parameter are found, then the selected deallocation function shall be the | |
4614 | one with two parameters. */ | |
4615 | tree del_coro_fr | |
4616 | = build_coroutine_op_delete_call (DELETE_EXPR, frame_arg, frame_size, | |
4617 | /*global_p=*/false, /*placement=*/NULL, | |
4618 | /*alloc_fn=*/NULL, tf_warning_or_error); | |
4619 | if (!del_coro_fr || del_coro_fr == error_mark_node) | |
4620 | return error_mark_node; | |
4621 | return del_coro_fr; | |
4622 | } | |
4623 | ||
4624 | /* Build the ramp function. | |
4625 | Here we take the original function definition which has now had its body | |
4626 | removed, and use it as the declaration of the ramp which both replaces the | |
4627 | user's written function at call sites, and is responsible for starting | |
4628 | the coroutine it defined. | |
cbe1b1c6 | 4629 | returns false on error. |
624fb5b4 IS |
4630 | |
4631 | We should arrive here with the state of the compiler as if we had just | |
4632 | executed start_preparsed_function(). */ | |
4633 | ||
4634 | bool | |
4635 | cp_coroutine_transform::build_ramp_function () | |
4636 | { | |
4637 | gcc_checking_assert (current_binding_level | |
4638 | && current_binding_level->kind == sk_function_parms); | |
4639 | ||
4640 | /* This is completely synthetic code, if we find an issue then we have not | |
4641 | much chance to point at the most useful place in the user's code. In | |
4642 | lieu of this use the function start - so at least the diagnostic relates | |
4643 | to something that the user can inspect. */ | |
4644 | iloc_sentinel saved_position (fn_start); | |
4645 | location_t loc = fn_start; | |
4646 | ||
4647 | tree promise_type = get_coroutine_promise_type (orig_fn_decl); | |
4648 | tree fn_return_type = TREE_TYPE (TREE_TYPE (orig_fn_decl)); | |
a0b43103 | 4649 | bool void_ramp_p = VOID_TYPE_P (fn_return_type); |
68ee624b IS |
4650 | /* We know there was no return statement, that is intentional. */ |
4651 | suppress_warning (orig_fn_decl, OPT_Wreturn_type); | |
624fb5b4 IS |
4652 | |
4653 | /* [dcl.fct.def.coroutine] / 10 (part1) | |
4654 | The unqualified-id get_return_object_on_allocation_failure is looked up | |
4655 | in the scope of the promise type by class member access lookup. */ | |
4656 | ||
4657 | /* We don't require this, but, if the lookup succeeds, then the function | |
4658 | must be usable, punt if it is not. */ | |
4659 | tree grooaf_meth | |
4660 | = lookup_promise_method (orig_fn_decl, | |
4661 | coro_gro_on_allocation_fail_identifier, loc, | |
4662 | /*musthave*/ false); | |
4663 | tree grooaf = NULL_TREE; | |
4664 | tree dummy_promise | |
4665 | = build_dummy_object (get_coroutine_promise_type (orig_fn_decl)); | |
4666 | if (grooaf_meth && grooaf_meth != error_mark_node) | |
4667 | { | |
4668 | grooaf | |
4669 | = coro_build_promise_expression (orig_fn_decl, dummy_promise, | |
4670 | coro_gro_on_allocation_fail_identifier, | |
4671 | fn_start, NULL, /*musthave=*/false); | |
4672 | ||
4673 | /* That should succeed. */ | |
4674 | if (!grooaf || grooaf == error_mark_node) | |
4675 | { | |
4676 | error_at (fn_start, "%qE is provided by %qT but is not usable with" | |
4677 | " the function %qD", coro_gro_on_allocation_fail_identifier, | |
4678 | promise_type, orig_fn_decl); | |
4679 | return false; | |
4680 | } | |
4681 | } | |
4682 | ||
4683 | /* Check early for usable allocator/deallocator, without which we cannot | |
4684 | build a useful ramp; early exit if they are not available or usable. */ | |
4685 | ||
4686 | frame_size = TYPE_SIZE_UNIT (frame_type); | |
4687 | ||
cbe1b1c6 | 4688 | /* Make a var to represent the frame pointer early. */ |
624fb5b4 IS |
4689 | tree coro_fp = coro_build_artificial_var (loc, "_Coro_frameptr", |
4690 | frame_ptr_type, orig_fn_decl, | |
4691 | NULL_TREE); | |
4692 | ||
4693 | tree new_fn_call | |
4694 | = build_coroutine_frame_alloc_expr (promise_type, orig_fn_decl, fn_start, | |
4695 | grooaf, ¶m_uses, frame_size); | |
4696 | ||
4697 | /* We must have a useable allocator to proceed. */ | |
4698 | if (!new_fn_call || new_fn_call == error_mark_node) | |
4699 | return false; | |
4700 | ||
4701 | /* Likewise, we need the DTOR to delete the frame. */ | |
4702 | tree delete_frame_call | |
4703 | = build_coroutine_frame_delete_expr (coro_fp, frame_size, promise_type, | |
4704 | fn_start); | |
4705 | if (!delete_frame_call || delete_frame_call == error_mark_node) | |
4706 | return false; | |
4707 | ||
4708 | /* At least verify we can lookup the get return object method. */ | |
4709 | tree get_ro_meth | |
4710 | = lookup_promise_method (orig_fn_decl, | |
4711 | coro_get_return_object_identifier, loc, | |
4712 | /*musthave*/ true); | |
4713 | if (!get_ro_meth || get_ro_meth == error_mark_node) | |
4714 | return false; | |
4715 | ||
4716 | /* So now construct the Ramp: */ | |
4717 | ||
cbe1b1c6 IS |
4718 | tree ramp_fnbody = begin_compound_stmt (BCS_FN_BODY); |
4719 | coro_fp = pushdecl (coro_fp); | |
4720 | add_decl_expr (coro_fp); | |
624fb5b4 | 4721 | |
cbe1b1c6 IS |
4722 | tree coro_promise_live = NULL_TREE; |
4723 | tree coro_gro_live = NULL_TREE; | |
4724 | if (flag_exceptions) | |
4725 | { | |
4726 | /* Signal that we need to clean up the promise object on exception. */ | |
4727 | coro_promise_live | |
4728 | = coro_build_and_push_artificial_var (loc, "_Coro_promise_live", | |
4729 | boolean_type_node, orig_fn_decl, | |
4730 | boolean_false_node); | |
4731 | ||
4732 | /* When the get-return-object is in the RETURN slot, we need to arrange | |
4733 | for cleanup on exception. */ | |
4734 | coro_gro_live | |
4735 | = coro_build_and_push_artificial_var (loc, "_Coro_gro_live", | |
4736 | boolean_type_node, orig_fn_decl, | |
4737 | boolean_false_node); | |
4738 | ||
4739 | /* To signal that we need to cleanup copied function args. */ | |
4740 | if (DECL_ARGUMENTS (orig_fn_decl)) | |
4741 | for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; | |
4742 | arg = DECL_CHAIN (arg)) | |
4743 | { | |
4744 | param_info *parm_i = param_uses.get (arg); | |
4745 | if (parm_i->trivial_dtor) | |
4746 | continue; | |
4747 | parm_i->guard_var = pushdecl (parm_i->guard_var); | |
4748 | add_decl_expr (parm_i->guard_var); | |
4749 | } | |
4750 | } | |
624fb5b4 | 4751 | |
cbe1b1c6 IS |
4752 | /* deref the frame pointer, to use in member access code. */ |
4753 | tree deref_fp | |
4754 | = cp_build_indirect_ref (loc, coro_fp, RO_UNARY_STAR, | |
4755 | tf_warning_or_error); | |
4756 | tree frame_needs_free | |
4757 | = coro_build_and_push_artificial_var_with_dve (loc, | |
4758 | coro_frame_needs_free_id, | |
4759 | boolean_type_node, | |
4760 | orig_fn_decl, NULL_TREE, | |
4761 | deref_fp); | |
49789fd0 | 4762 | |
624fb5b4 IS |
4763 | /* Build the frame. */ |
4764 | ||
4765 | /* The CO_FRAME internal function is a mechanism to allow the middle end | |
4766 | to adjust the allocation in response to optimizations. We provide the | |
4767 | current conservative estimate of the frame size (as per the current) | |
4768 | computed layout. */ | |
4769 | ||
4770 | tree resizeable | |
4771 | = build_call_expr_internal_loc (loc, IFN_CO_FRAME, size_type_node, 2, | |
4772 | frame_size, | |
4773 | build_zero_cst (frame_ptr_type)); | |
4774 | CALL_EXPR_ARG (new_fn_call, 0) = resizeable; | |
4775 | tree allocated = build1 (CONVERT_EXPR, frame_ptr_type, new_fn_call); | |
6ffbf87c | 4776 | tree r = cp_build_init_expr (coro_fp, allocated); |
6303cd7e | 4777 | finish_expr_stmt (r); |
49789fd0 IS |
4778 | |
4779 | /* If the user provided a method to return an object on alloc fail, then | |
4780 | check the returned pointer and call the func if it's null. | |
4781 | Otherwise, no check, and we fail for noexcept/fno-exceptions cases. */ | |
4782 | ||
4783 | if (grooaf) | |
4784 | { | |
80644a67 | 4785 | /* [dcl.fct.def.coroutine] / 10 (part 3) |
68bb7e3b IS |
4786 | If the allocation function returns nullptr,the coroutine returns |
4787 | control to the caller of the coroutine and the return value is | |
4788 | obtained by a call to T::get_return_object_on_allocation_failure(), | |
4789 | where T is the promise type. */ | |
b9c91b7f | 4790 | tree if_stmt = begin_if_stmt (); |
6303cd7e | 4791 | tree cond = build1 (CONVERT_EXPR, frame_ptr_type, nullptr_node); |
b9c91b7f IS |
4792 | cond = build2 (EQ_EXPR, boolean_type_node, coro_fp, cond); |
4793 | finish_if_stmt_cond (cond, if_stmt); | |
f4915e6c | 4794 | r = NULL_TREE; |
a0b43103 | 4795 | if (void_ramp_p) |
f4915e6c IS |
4796 | /* Execute the get-return-object-on-alloc-fail call... */ |
4797 | finish_expr_stmt (grooaf); | |
b9c91b7f | 4798 | else |
f4915e6c IS |
4799 | /* Get the fallback return object. */ |
4800 | r = grooaf; | |
4801 | finish_return_stmt (r); | |
b9c91b7f IS |
4802 | finish_then_clause (if_stmt); |
4803 | finish_if_stmt (if_stmt); | |
49789fd0 IS |
4804 | } |
4805 | ||
cbe1b1c6 IS |
4806 | /* For now, once allocation has succeeded we always assume that this needs |
4807 | destruction, there's no impl. for frame allocation elision. */ | |
4808 | r = cp_build_init_expr (frame_needs_free, boolean_true_node); | |
4809 | finish_expr_stmt (r); | |
4810 | ||
4811 | /* Set up the promise. */ | |
4812 | tree p | |
4813 | = coro_build_and_push_artificial_var_with_dve (loc, coro_promise_id, | |
4814 | promise_type, orig_fn_decl, | |
4815 | NULL_TREE, deref_fp); | |
4816 | ||
fe550865 IS |
4817 | /* Up to now any exception thrown will propagate directly to the caller. |
4818 | This is OK since the only source of such exceptions would be in allocation | |
4819 | of the coroutine frame, and therefore the ramp will not have initialized | |
4820 | any further state. From here, we will track state that needs explicit | |
4821 | destruction in the case that promise or g.r.o setup fails or an exception | |
4822 | is thrown from the initial suspend expression. */ | |
cbe1b1c6 IS |
4823 | tree ramp_try_block = NULL_TREE; |
4824 | tree ramp_try_stmts = NULL_TREE; | |
4825 | tree iarc_x = NULL_TREE; | |
fe550865 IS |
4826 | if (flag_exceptions) |
4827 | { | |
cbe1b1c6 IS |
4828 | iarc_x |
4829 | = coro_build_and_push_artificial_var_with_dve (loc, | |
4830 | coro_frame_i_a_r_c_id, | |
4831 | boolean_type_node, | |
4832 | orig_fn_decl, NULL_TREE, | |
4833 | deref_fp); | |
4834 | ramp_try_block = begin_try_block (); | |
4835 | ramp_try_stmts = begin_compound_stmt (BCS_TRY_BLOCK); | |
fe550865 IS |
4836 | } |
4837 | ||
49789fd0 IS |
4838 | /* Put the resumer and destroyer functions in. */ |
4839 | ||
6303cd7e | 4840 | tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr_type, resumer); |
cbe1b1c6 IS |
4841 | coro_build_and_push_artificial_var_with_dve (loc, coro_resume_fn_id, |
4842 | act_des_fn_ptr_type, | |
4843 | orig_fn_decl, | |
4844 | actor_addr, deref_fp); | |
49789fd0 | 4845 | |
6303cd7e | 4846 | tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr_type, destroyer); |
cbe1b1c6 IS |
4847 | coro_build_and_push_artificial_var_with_dve (loc, coro_destroy_fn_id, |
4848 | act_des_fn_ptr_type, | |
4849 | orig_fn_decl, | |
4850 | destroy_addr, deref_fp); | |
49789fd0 | 4851 | |
80644a67 | 4852 | /* [dcl.fct.def.coroutine] /13 |
dc192bbd IS |
4853 | When a coroutine is invoked, a copy is created for each coroutine |
4854 | parameter. Each such copy is an object with automatic storage duration | |
4855 | that is direct-initialized from an lvalue referring to the corresponding | |
4856 | parameter if the parameter is an lvalue reference, and from an xvalue | |
4857 | referring to it otherwise. A reference to a parameter in the function- | |
4858 | body of the coroutine and in the call to the coroutine promise | |
4859 | constructor is replaced by a reference to its copy. */ | |
49789fd0 | 4860 | |
dc192bbd | 4861 | vec<tree, va_gc> *promise_args = NULL; /* So that we can adjust refs. */ |
49789fd0 | 4862 | |
dc192bbd IS |
4863 | /* The initialization and destruction of each parameter copy occurs in the |
4864 | context of the called coroutine. Initializations of parameter copies are | |
4865 | sequenced before the call to the coroutine promise constructor and | |
4866 | indeterminately sequenced with respect to each other. The lifetime of | |
4867 | parameter copies ends immediately after the lifetime of the coroutine | |
4868 | promise object ends. */ | |
49789fd0 | 4869 | |
6303cd7e | 4870 | if (DECL_ARGUMENTS (orig_fn_decl)) |
49789fd0 | 4871 | { |
dc192bbd | 4872 | promise_args = make_tree_vector (); |
6303cd7e | 4873 | for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; |
dc192bbd | 4874 | arg = DECL_CHAIN (arg)) |
49789fd0 IS |
4875 | { |
4876 | bool existed; | |
6303cd7e | 4877 | param_info &parm = param_uses.get_or_insert (arg, &existed); |
49789fd0 | 4878 | tree fld_idx |
049a927c IS |
4879 | = coro_build_frame_access_expr (deref_fp, parm.field_id, |
4880 | false, tf_warning_or_error); | |
49789fd0 | 4881 | |
dc192bbd | 4882 | /* Add this to the promise CTOR arguments list, accounting for |
f5edc76a | 4883 | refs and special handling for method this ptr. */ |
1c140cfb | 4884 | if (parm.this_ptr || parm.lambda_cobj) |
38644f81 IS |
4885 | { |
4886 | /* We pass a reference to *this to the param preview. */ | |
303bed67 PP |
4887 | /* It's unsafe to use the cp_ version here since current_class_ref |
4888 | might've gotten clobbered earlier during rewrite_param_uses. */ | |
4889 | tree this_ref = build_fold_indirect_ref (arg); | |
38644f81 IS |
4890 | vec_safe_push (promise_args, this_ref); |
4891 | } | |
b8ff3f8e | 4892 | else if (parm.rv_ref) |
14ed21f8 | 4893 | vec_safe_push (promise_args, move (fld_idx)); |
dc192bbd | 4894 | else |
b8ff3f8e | 4895 | vec_safe_push (promise_args, fld_idx); |
dc192bbd | 4896 | |
b8ff3f8e IS |
4897 | if (parm.rv_ref || parm.pt_ref) |
4898 | /* Initialise the frame reference field directly. */ | |
6303cd7e | 4899 | r = cp_build_modify_expr (loc, TREE_OPERAND (fld_idx, 0), |
14ed21f8 | 4900 | INIT_EXPR, arg, tf_warning_or_error); |
49789fd0 IS |
4901 | else |
4902 | { | |
14ed21f8 | 4903 | r = forward_parm (arg); |
6303cd7e | 4904 | r = cp_build_modify_expr (loc, fld_idx, INIT_EXPR, r, |
14ed21f8 | 4905 | tf_warning_or_error); |
49789fd0 | 4906 | } |
fe550865 | 4907 | finish_expr_stmt (r); |
dc192bbd IS |
4908 | if (!parm.trivial_dtor) |
4909 | { | |
6303cd7e | 4910 | param_dtor_list.safe_push (parm.field_id); |
fe550865 IS |
4911 | /* Cleanup this frame copy on exception. */ |
4912 | parm.fr_copy_dtor | |
44868e72 | 4913 | = cxx_maybe_build_cleanup (fld_idx, tf_warning_or_error); |
fae62716 IS |
4914 | if (flag_exceptions) |
4915 | { | |
4916 | /* This var is now live. */ | |
6303cd7e IS |
4917 | r = build_modify_expr (loc, parm.guard_var, |
4918 | boolean_type_node, INIT_EXPR, loc, | |
fae62716 IS |
4919 | boolean_true_node, boolean_type_node); |
4920 | finish_expr_stmt (r); | |
4921 | } | |
dc192bbd IS |
4922 | } |
4923 | } | |
4924 | } | |
4925 | ||
3d9577c2 | 4926 | if (type_build_ctor_call (promise_type)) |
dc192bbd | 4927 | { |
7d148392 | 4928 | /* Construct the promise object [dcl.fct.def.coroutine] / 5.7. |
dc192bbd | 4929 | |
7d148392 IS |
4930 | First try to find a constructor with an argument list comprised of |
4931 | the parameter copies. */ | |
dc192bbd | 4932 | |
6303cd7e | 4933 | if (DECL_ARGUMENTS (orig_fn_decl)) |
dc192bbd IS |
4934 | { |
4935 | r = build_special_member_call (p, complete_ctor_identifier, | |
4936 | &promise_args, promise_type, | |
4937 | LOOKUP_NORMAL, tf_none); | |
4938 | release_tree_vector (promise_args); | |
49789fd0 | 4939 | } |
dc192bbd IS |
4940 | else |
4941 | r = NULL_TREE; | |
4942 | ||
7d148392 | 4943 | /* If that fails then the promise constructor argument list is empty. */ |
dc192bbd IS |
4944 | if (r == NULL_TREE || r == error_mark_node) |
4945 | r = build_special_member_call (p, complete_ctor_identifier, NULL, | |
4946 | promise_type, LOOKUP_NORMAL, | |
4947 | tf_warning_or_error); | |
4948 | ||
7d148392 IS |
4949 | /* If type_build_ctor_call() encounters deprecated implicit CTORs it will |
4950 | return true, and therefore we will execute this code path. However, | |
4951 | we might well not actually require a CTOR and under those conditions | |
4952 | the build call above will not return a call expression, but the | |
4953 | original instance object. Do not attempt to add the statement unless | |
4954 | it has side-effects. */ | |
4955 | if (r && r != error_mark_node && TREE_SIDE_EFFECTS (r)) | |
4956 | finish_expr_stmt (r); | |
4957 | } | |
fe550865 | 4958 | |
7d148392 IS |
4959 | tree promise_dtor = cxx_maybe_build_cleanup (p, tf_warning_or_error);; |
4960 | if (flag_exceptions && promise_dtor) | |
4961 | { | |
4962 | r = cp_build_init_expr (coro_promise_live, boolean_true_node); | |
fe550865 | 4963 | finish_expr_stmt (r); |
49789fd0 IS |
4964 | } |
4965 | ||
49789fd0 | 4966 | tree get_ro |
6303cd7e | 4967 | = coro_build_promise_expression (orig_fn_decl, p, |
e74c7607 IS |
4968 | coro_get_return_object_identifier, |
4969 | fn_start, NULL, /*musthave=*/true); | |
68ee624b | 4970 | |
49789fd0 | 4971 | /* Without a return object we haven't got much clue what's going on. */ |
a0b43103 | 4972 | if (!get_ro || get_ro == error_mark_node) |
cbe1b1c6 | 4973 | return false; |
68ee624b | 4974 | |
a0b43103 IS |
4975 | /* Check for a bad get return object type. |
4976 | [dcl.fct.def.coroutine] / 7 requires: | |
4977 | The expression promise.get_return_object() is used to initialize the | |
4978 | returned reference or prvalue result object ... */ | |
4979 | tree gro_type = TREE_TYPE (get_ro); | |
4980 | if (VOID_TYPE_P (gro_type) && !void_ramp_p) | |
4981 | { | |
4982 | error_at (fn_start, "no viable conversion from %<void%> provided by" | |
4983 | " %<get_return_object%> to return type %qT", fn_return_type); | |
4984 | return false; | |
4985 | } | |
49789fd0 | 4986 | |
68ee624b | 4987 | /* Initialize the resume_idx_var to 0, meaning "not started". */ |
cbe1b1c6 IS |
4988 | coro_build_and_push_artificial_var_with_dve |
4989 | (loc, coro_resume_index_id, short_unsigned_type_node, orig_fn_decl, | |
4990 | build_zero_cst (short_unsigned_type_node), deref_fp); | |
4991 | ||
4992 | if (flag_exceptions && iarc_x) | |
4993 | { | |
4994 | r = cp_build_init_expr (iarc_x, boolean_false_node); | |
4995 | finish_expr_stmt (r); | |
4996 | } | |
cd08718d | 4997 | |
fe550865 | 4998 | /* Used for return objects in the RESULT slot. */ |
68ee624b | 4999 | tree ret_val_dtor = NULL_TREE; |
fe550865 | 5000 | |
68ee624b IS |
5001 | /* [dcl.fct.def.coroutine] / 7 |
5002 | The expression promise.get_return_object() is used to initialize the | |
5003 | glvalue result or prvalue result object of a call to a coroutine. */ | |
5004 | ||
f0315f7a IS |
5005 | /* We must manage the cleanups ourselves, because the responsibility for |
5006 | them changes after the initial suspend. However, any use of | |
5007 | cxx_maybe_build_cleanup () can set the throwing_cleanup flag. */ | |
5008 | cp_function_chain->throwing_cleanup = false; | |
68ee624b IS |
5009 | if (void_ramp_p) |
5010 | /* We still want to call the method, even if the result is unused. */ | |
5011 | r = get_ro; | |
b9c91b7f IS |
5012 | else |
5013 | { | |
f0315f7a | 5014 | /* The initial section of finish_return_expr (). */ |
68ee624b IS |
5015 | bool no_warning; |
5016 | bool dangling; | |
5017 | /* Without a relevant location, bad conversions in check_return_expr | |
5018 | result in unusable diagnostics, since there is not even a mention | |
5019 | of the relevant function. Here we carry out the first part of | |
5020 | finish_return_expr(). */ | |
5021 | input_location = fn_start; | |
5022 | r = check_return_expr (get_ro, &no_warning, &dangling); | |
5023 | input_location = UNKNOWN_LOCATION; | |
5024 | gcc_checking_assert (!dangling); | |
5025 | /* Check for bad things. */ | |
5026 | if (!r || r == error_mark_node) | |
5027 | return false; | |
b9c91b7f | 5028 | } |
68ee624b | 5029 | |
788b962a IS |
5030 | finish_expr_stmt (r); |
5031 | ||
68ee624b IS |
5032 | if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (fn_return_type)) |
5033 | /* If some part of the initalization code (prior to the await_resume | |
5034 | of the initial suspend expression), then we need to clean up the | |
5035 | return value. */ | |
5036 | ret_val_dtor = cxx_maybe_build_cleanup (DECL_RESULT (orig_fn_decl), | |
cbe1b1c6 | 5037 | tf_warning_or_error); |
49789fd0 | 5038 | |
fe550865 IS |
5039 | /* If we have a live g.r.o in the return slot, then signal this for exception |
5040 | cleanup. */ | |
cbe1b1c6 | 5041 | if (flag_exceptions && ret_val_dtor) |
fe550865 | 5042 | { |
cbe1b1c6 | 5043 | r = cp_build_init_expr (coro_gro_live, boolean_true_node); |
fe550865 IS |
5044 | finish_expr_stmt (r); |
5045 | } | |
49789fd0 | 5046 | |
68ee624b | 5047 | /* Start the coroutine body. */ |
6303cd7e IS |
5048 | r = build_call_expr_loc (fn_start, resumer, 1, coro_fp); |
5049 | finish_expr_stmt (r); | |
49789fd0 | 5050 | |
68ee624b IS |
5051 | /* The ramp is done, we just need the return statement, which we build from |
5052 | the return object we constructed before we called the actor. */ | |
4f2d05ef | 5053 | |
68ee624b | 5054 | r = void_ramp_p ? NULL_TREE : DECL_RESULT (orig_fn_decl); |
1f2e8423 | 5055 | |
68ee624b IS |
5056 | /* The reminder of finish_return_expr (). */ |
5057 | r = build_stmt (loc, RETURN_EXPR, r); | |
5058 | r = maybe_cleanup_point_expr_void (r); | |
5059 | r = add_stmt (r); | |
fe550865 IS |
5060 | |
5061 | if (flag_exceptions) | |
5062 | { | |
cbe1b1c6 IS |
5063 | finish_compound_stmt (ramp_try_stmts); |
5064 | finish_try_block (ramp_try_block); | |
fe550865 IS |
5065 | tree handler = begin_handler (); |
5066 | finish_handler_parms (NULL_TREE, handler); /* catch (...) */ | |
5067 | ||
68ee624b IS |
5068 | /* If we have a live G.R.O in the return slot, then run its DTOR. */ |
5069 | if (ret_val_dtor && ret_val_dtor != error_mark_node) | |
fe550865 IS |
5070 | { |
5071 | tree gro_d_if = begin_if_stmt (); | |
5072 | finish_if_stmt_cond (coro_gro_live, gro_d_if); | |
68ee624b | 5073 | finish_expr_stmt (ret_val_dtor); |
fe550865 | 5074 | finish_then_clause (gro_d_if); |
efc99ab2 | 5075 | finish_if_stmt (gro_d_if); |
fe550865 IS |
5076 | } |
5077 | ||
efc99ab2 IS |
5078 | /* Before initial resume is called, the responsibility for cleanup on |
5079 | exception falls to the ramp. After that, the coroutine body code | |
cbe1b1c6 IS |
5080 | should do the cleanup. This is signalled by the flag |
5081 | 'initial_await_resume_called'. */ | |
5082 | ||
efc99ab2 IS |
5083 | tree not_iarc |
5084 | = build1_loc (loc, TRUTH_NOT_EXPR, boolean_type_node, iarc_x); | |
5085 | tree cleanup_if = begin_if_stmt (); | |
5086 | finish_if_stmt_cond (not_iarc, cleanup_if); | |
fe550865 IS |
5087 | /* If the promise is live, then run its dtor if that's available. */ |
5088 | if (promise_dtor && promise_dtor != error_mark_node) | |
5089 | { | |
5090 | tree promise_d_if = begin_if_stmt (); | |
5091 | finish_if_stmt_cond (coro_promise_live, promise_d_if); | |
5092 | finish_expr_stmt (promise_dtor); | |
5093 | finish_then_clause (promise_d_if); | |
efc99ab2 | 5094 | finish_if_stmt (promise_d_if); |
fe550865 IS |
5095 | } |
5096 | ||
d5da5f00 IS |
5097 | /* Clean up any frame copies of parms with non-trivial dtors. |
5098 | Do this in reverse order from their creation. */ | |
5099 | auto_vec<param_info *> worklist; | |
6303cd7e IS |
5100 | if (DECL_ARGUMENTS (orig_fn_decl)) |
5101 | for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; | |
fe550865 IS |
5102 | arg = DECL_CHAIN (arg)) |
5103 | { | |
6303cd7e | 5104 | param_info *parm_i = param_uses.get (arg); |
fe550865 IS |
5105 | if (parm_i->trivial_dtor) |
5106 | continue; | |
d5da5f00 | 5107 | worklist.safe_push (parm_i); |
fe550865 | 5108 | } |
6303cd7e | 5109 | while (!worklist.is_empty ()) |
d5da5f00 IS |
5110 | { |
5111 | param_info *parm_i = worklist.pop (); | |
5112 | if (parm_i->fr_copy_dtor && parm_i->fr_copy_dtor != error_mark_node) | |
5113 | { | |
5114 | tree dtor_if = begin_if_stmt (); | |
5115 | finish_if_stmt_cond (parm_i->guard_var, dtor_if); | |
5116 | finish_expr_stmt (parm_i->fr_copy_dtor); | |
5117 | finish_then_clause (dtor_if); | |
efc99ab2 | 5118 | finish_if_stmt (dtor_if); |
d5da5f00 IS |
5119 | } |
5120 | } | |
fe550865 | 5121 | |
efc99ab2 IS |
5122 | /* No delete the frame if required. */ |
5123 | tree fnf_if = begin_if_stmt (); | |
cbe1b1c6 | 5124 | finish_if_stmt_cond (frame_needs_free, fnf_if); |
624fb5b4 | 5125 | finish_expr_stmt (delete_frame_call); |
efc99ab2 IS |
5126 | finish_then_clause (fnf_if); |
5127 | finish_if_stmt (fnf_if); | |
5128 | ||
5129 | /* Finished cleanups conditional on "initial resume is not called". */ | |
5130 | finish_then_clause (cleanup_if); | |
5131 | finish_if_stmt (cleanup_if); | |
5132 | ||
6303cd7e | 5133 | tree rethrow = build_throw (loc, NULL_TREE, tf_warning_or_error); |
65870e75 | 5134 | suppress_warning (rethrow); |
fe550865 IS |
5135 | finish_expr_stmt (rethrow); |
5136 | finish_handler (handler); | |
cbe1b1c6 | 5137 | finish_handler_sequence (ramp_try_block); |
fe550865 | 5138 | } |
cbe1b1c6 | 5139 | finish_compound_stmt (ramp_fnbody); |
624fb5b4 | 5140 | return true; |
6303cd7e IS |
5141 | } |
5142 | ||
5143 | /* ------- Encapsulate analysis of the couroutine -------- */ | |
5144 | ||
5145 | ||
5146 | cp_coroutine_transform::cp_coroutine_transform (tree _orig_fn, bool _inl) | |
5147 | : orig_fn_decl (_orig_fn), inline_p (_inl) | |
5148 | { | |
5149 | /* We don't expect to be called with missing decl or e_m_n. */ | |
5150 | gcc_checking_assert (orig_fn_decl | |
5151 | && TREE_CODE (orig_fn_decl) == FUNCTION_DECL); | |
5152 | if (!coro_function_valid_p (orig_fn_decl)) | |
5153 | { | |
5154 | /* For early errors, we do not want a diagnostic about the missing | |
5155 | ramp return value, since the user cannot fix this - a 'return' is | |
5156 | not allowed in a coroutine. */ | |
5157 | suppress_warning (orig_fn_decl, OPT_Wreturn_type); | |
5158 | /* Discard the body, we can't process it further... */ | |
5159 | pop_stmt_list (DECL_SAVED_TREE (orig_fn_decl)); | |
5160 | /* ... and make an empty fn. */ | |
5161 | DECL_SAVED_TREE (orig_fn_decl) = push_stmt_list (); | |
5162 | /* Match the expected nesting when an eh block is in use. */ | |
5163 | if (use_eh_spec_block (orig_fn_decl)) | |
5164 | current_eh_spec_block = begin_eh_spec_block (); | |
5165 | valid_coroutine = false; | |
5166 | } | |
5167 | ||
5168 | /* We don't have the locus of the opening brace - it's filled in later (and | |
5169 | there doesn't really seem to be any easy way to get at it). | |
5170 | The closing brace is assumed to be input_location. */ | |
5171 | fn_start = DECL_SOURCE_LOCATION (orig_fn_decl); | |
5172 | ||
5173 | /* Build types we need. */ | |
5174 | tree fr_name = get_fn_local_identifier (orig_fn_decl, "Frame"); | |
5175 | frame_type = xref_tag (record_type, fr_name); | |
5176 | DECL_CONTEXT (TYPE_NAME (frame_type)) = DECL_CONTEXT (orig_fn_decl); | |
5177 | frame_ptr_type = build_pointer_type (frame_type); | |
5178 | act_des_fn_type | |
5179 | = build_function_type_list (void_type_node, frame_ptr_type, NULL_TREE); | |
5180 | act_des_fn_ptr_type = build_pointer_type (act_des_fn_type); | |
5181 | valid_coroutine = true; | |
5182 | } | |
5183 | ||
5184 | cp_coroutine_transform::~cp_coroutine_transform () | |
5185 | { | |
d5da5f00 IS |
5186 | } |
5187 | ||
5188 | /* Here we: | |
5189 | a) Check that the function and promise type are valid for a | |
5190 | coroutine. | |
5191 | b) Carry out the initial morph to create the skeleton of the | |
5192 | coroutine ramp function and the rewritten body. | |
5193 | ||
5194 | Assumptions. | |
5195 | ||
5196 | 1. We only hit this code once all dependencies are resolved. | |
5197 | 2. The function body will be either a bind expr or a statement list | |
5198 | 3. That cfun and current_function_decl are valid for the case we're | |
5199 | expanding. | |
5200 | 4. 'input_location' will be of the final brace for the function. | |
5201 | ||
5202 | We do something like this: | |
5203 | declare a dummy coro frame. | |
5204 | struct _R_frame { | |
5205 | using handle_type = coro::coroutine_handle<coro1::promise_type>; | |
5206 | void (*_Coro_resume_fn)(_R_frame *); | |
5207 | void (*_Coro_destroy_fn)(_R_frame *); | |
5208 | coro1::promise_type _Coro_promise; | |
5209 | bool _Coro_frame_needs_free; free the coro frame mem if set. | |
5210 | bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3 | |
5211 | short _Coro_resume_index; | |
5212 | handle_type _Coro_self_handle; | |
5213 | parameter copies (were required). | |
5214 | local variables saved (including awaitables) | |
5215 | (maybe) trailing space. | |
5216 | }; */ | |
5217 | ||
6303cd7e IS |
5218 | void |
5219 | cp_coroutine_transform::apply_transforms () | |
d5da5f00 | 5220 | { |
d5da5f00 | 5221 | |
6303cd7e IS |
5222 | coroutine_body |
5223 | = split_coroutine_body_from_ramp (orig_fn_decl); | |
5224 | if (!coroutine_body) | |
d5da5f00 | 5225 | { |
6303cd7e IS |
5226 | valid_coroutine = false; |
5227 | return; | |
d5da5f00 | 5228 | } |
6303cd7e IS |
5229 | /* Keep the original function block tree to one side and reset. */ |
5230 | body_blocks = current_binding_level->blocks; | |
5231 | current_binding_level->blocks = NULL_TREE; | |
d5da5f00 | 5232 | |
6303cd7e IS |
5233 | /* Collect information on the original function params and their use in the |
5234 | function body. */ | |
5235 | analyze_fn_parms (orig_fn_decl, ¶m_uses); | |
d5da5f00 | 5236 | |
6303cd7e IS |
5237 | /* Declare the actor and destroyer functions, the following code needs to |
5238 | see these. */ | |
5239 | resumer | |
5240 | = coro_build_actor_or_destroy_function (orig_fn_decl, act_des_fn_type, | |
5241 | frame_ptr_type, true); | |
5242 | destroyer | |
5243 | = coro_build_actor_or_destroy_function (orig_fn_decl, act_des_fn_type, | |
5244 | frame_ptr_type, false); | |
d5da5f00 | 5245 | |
6303cd7e IS |
5246 | /* Transform the function body as per [dcl.fct.def.coroutine] / 5. */ |
5247 | wrap_original_function_body (); | |
d5da5f00 | 5248 | |
6303cd7e | 5249 | /* Analyze the body await expressions. */ |
d5da5f00 | 5250 | susp_frame_data body_aw_points (fs_label, &suspend_points); |
6303cd7e IS |
5251 | cp_walk_tree (&coroutine_body, await_statement_walker, &body_aw_points, NULL); |
5252 | await_count = body_aw_points.await_number; | |
d5da5f00 | 5253 | |
6303cd7e | 5254 | /* Determine the fields for the coroutine state. */ |
d5da5f00 | 5255 | tree field_list = NULL_TREE; |
d5da5f00 | 5256 | local_vars_frame_data local_vars_data (&field_list, &local_var_uses); |
6303cd7e | 5257 | cp_walk_tree (&coroutine_body, register_local_var_uses, &local_vars_data, NULL); |
49789fd0 | 5258 | |
6303cd7e IS |
5259 | /* Conservative computation of the coroutine frame content. */ |
5260 | frame_type = begin_class_definition (frame_type); | |
5261 | TYPE_FIELDS (frame_type) = field_list; | |
5262 | TYPE_BINFO (frame_type) = make_tree_binfo (0); | |
5263 | BINFO_OFFSET (TYPE_BINFO (frame_type)) = size_zero_node; | |
5264 | BINFO_TYPE (TYPE_BINFO (frame_type)) = frame_type; | |
5265 | frame_type = finish_struct (frame_type, NULL_TREE); | |
49789fd0 | 5266 | |
624fb5b4 | 5267 | valid_coroutine = build_ramp_function (); |
6303cd7e | 5268 | } |
49789fd0 | 5269 | |
6303cd7e IS |
5270 | /* Having analysed and collected the necessary data we are now in a position |
5271 | to build the outlined coroutine body and the destroyer shim. */ | |
49789fd0 | 5272 | |
6303cd7e IS |
5273 | void |
5274 | cp_coroutine_transform::finish_transforms () | |
5275 | { | |
5276 | if (!valid_coroutine) | |
5277 | return; | |
49789fd0 | 5278 | |
6303cd7e IS |
5279 | current_function_decl = resumer; |
5280 | build_actor_fn (fn_start, frame_type, resumer, coroutine_body, orig_fn_decl, | |
5281 | &local_var_uses, &suspend_points, ¶m_dtor_list, | |
5282 | resume_idx_var, await_count, frame_size, inline_p); | |
49789fd0 | 5283 | |
6303cd7e IS |
5284 | current_function_decl = destroyer; |
5285 | build_destroy_fn (fn_start, frame_type, destroyer, resumer, inline_p); | |
49789fd0 IS |
5286 | } |
5287 | ||
5288 | #include "gt-cp-coroutines.h" | |
5289 |