+2020-01-18 Iain Sandoe <iain@sandoe.co.uk>
+
+ * Makefile.in: Add coroutine-passes.o.
+ * builtin-types.def (BT_CONST_SIZE): New.
+ (BT_FN_BOOL_PTR): New.
+ (BT_FN_PTR_PTR_CONST_SIZE_BOOL): New.
+ * builtins.def (DEF_COROUTINE_BUILTIN): New.
+ * coroutine-builtins.def: New file.
+ * coroutine-passes.cc: New file.
+ * function.h (struct GTY function): Add a bit to indicate that the
+ function is a coroutine component.
+ * internal-fn.c (expand_CO_FRAME): New.
+ (expand_CO_YIELD): New.
+ (expand_CO_SUSPN): New.
+ (expand_CO_ACTOR): New.
+ * internal-fn.def (CO_ACTOR): New.
+ (CO_YIELD): New.
+ (CO_SUSPN): New.
+ (CO_FRAME): New.
+ * passes.def: Add pass_coroutine_lower_builtins,
+ pass_coroutine_early_expand_ifns.
+ * tree-pass.h (make_pass_coroutine_lower_builtins): New.
+ (make_pass_coroutine_early_expand_ifns): New.
+ * doc/invoke.texi: Document the fcoroutines command line
+ switch.
+
2020-01-18 Jakub Jelinek <jakub@redhat.com>
* config/arm/vfp.md (*clear_vfp_multiple): Remove unused variable.
compare-elim.o \
context.o \
convert.o \
+ coroutine-passes.o \
coverage.o \
cppbuiltin.o \
cppdefault.o \
DEF_PRIMITIVE_TYPE (BT_LONGDOUBLE_PTR, long_double_ptr_type_node)
DEF_PRIMITIVE_TYPE (BT_PID, pid_type_node)
DEF_PRIMITIVE_TYPE (BT_SIZE, size_type_node)
+DEF_PRIMITIVE_TYPE (BT_CONST_SIZE,
+ build_qualified_type (size_type_node, TYPE_QUAL_CONST))
DEF_PRIMITIVE_TYPE (BT_SSIZE, signed_size_type_node)
DEF_PRIMITIVE_TYPE (BT_WINT, wint_type_node)
DEF_PRIMITIVE_TYPE (BT_STRING, string_type_node)
DEF_FUNCTION_TYPE_1 (BT_FN_UINT64_UINT64, BT_UINT64, BT_UINT64)
DEF_FUNCTION_TYPE_1 (BT_FN_UINT64_FLOAT, BT_UINT64, BT_FLOAT)
DEF_FUNCTION_TYPE_1 (BT_FN_BOOL_INT, BT_BOOL, BT_INT)
+DEF_FUNCTION_TYPE_1 (BT_FN_BOOL_PTR, BT_BOOL, BT_PTR)
DEF_FUNCTION_TYPE_1 (BT_FN_PTR_CONST_PTR, BT_PTR, BT_CONST_PTR)
DEF_FUNCTION_TYPE_1 (BT_FN_CONST_PTR_CONST_PTR, BT_CONST_PTR, BT_CONST_PTR)
DEF_FUNCTION_TYPE_1 (BT_FN_UINT16_UINT32, BT_UINT16, BT_UINT32)
DEF_FUNCTION_TYPE_3 (BT_FN_VOID_SIZE_SIZE_PTR, BT_VOID, BT_SIZE, BT_SIZE,
BT_PTR)
DEF_FUNCTION_TYPE_3 (BT_FN_UINT_UINT_PTR_PTR, BT_UINT, BT_UINT, BT_PTR, BT_PTR)
+DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_CONST_SIZE_BOOL,
+ BT_PTR, BT_PTR, BT_CONST_SIZE, BT_BOOL)
DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
DEF_BUILTIN (ENUM, NAME, BUILT_IN_NORMAL, BT_LAST, BT_LAST, false, false, \
false, ATTR_LAST, false, false)
+/* Builtins used in implementing coroutine support. */
+#undef DEF_COROUTINE_BUILTIN
+#define DEF_COROUTINE_BUILTIN(ENUM, NAME, TYPE, ATTRS) \
+ DEF_BUILTIN (ENUM, "__builtin_coro_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \
+ true, true, true, ATTRS, true, flag_coroutines)
+
/* Builtin used by the implementation of OpenACC and OpenMP. Few of these are
actually implemented in the compiler; most are in libgomp. */
/* These builtins also need to be enabled in offloading compilers invoked from
/* Sanitizer builtins. */
#include "sanitizer.def"
+/* Coroutine builtins. */
+#include "coroutine-builtins.def"
+
/* Do not expose the BRIG builtins by default gcc-wide, but only privately in
the BRIG FE as long as there are no references for them in the middle end
or any of the upstream backends. */
+2020-01-18 Iain Sandoe <iain@sandoe.co.uk>
+
+ * c-common.c (co_await, co_yield, co_return): New.
+ * c-common.h (RID_CO_AWAIT, RID_CO_YIELD,
+ RID_CO_RETURN): New enumeration values.
+ (D_CXX_COROUTINES): Bit to identify coroutines are active.
+ (D_CXX_COROUTINES_FLAGS): Guard for coroutine keywords.
+ * c-cppbuiltin.c (__cpp_coroutines): New cpp define.
+ * c.opt (fcoroutines): New command-line switch.
+
2020-01-10 David Malcolm <dmalcolm@redhat.com>
* c-format.c (local_event_ptr_node): New.
{ "concept", RID_CONCEPT, D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
{ "requires", RID_REQUIRES, D_CXX_CONCEPTS_FLAGS | D_CXXWARN },
+ /* Coroutines-related keywords */
+ { "co_await", RID_CO_AWAIT, D_CXX_COROUTINES_FLAGS | D_CXXWARN },
+ { "co_yield", RID_CO_YIELD, D_CXX_COROUTINES_FLAGS | D_CXXWARN },
+ { "co_return", RID_CO_RETURN, D_CXX_COROUTINES_FLAGS | D_CXXWARN },
+
/* These Objective-C keywords are recognized only immediately after
an '@'. */
{ "compatibility_alias", RID_AT_ALIAS, D_OBJC },
/* C++ concepts */
RID_CONCEPT, RID_REQUIRES,
+ /* C++ coroutines */
+ RID_CO_AWAIT, RID_CO_YIELD, RID_CO_RETURN,
+
/* C++ transactional memory. */
RID_ATOMIC_NOEXCEPT, RID_ATOMIC_CANCEL, RID_SYNCHRONIZED,
#define D_TRANSMEM 0X0800 /* C++ transactional memory TS. */
#define D_CXX_CHAR8_T 0X1000 /* In C++, only with -fchar8_t. */
#define D_CXX20 0x2000 /* In C++, C++20 only. */
+#define D_CXX_COROUTINES 0x4000 /* In C++, only with coroutines. */
#define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
#define D_CXX_CHAR8_T_FLAGS D_CXXONLY | D_CXX_CHAR8_T
+#define D_CXX_COROUTINES_FLAGS (D_CXXONLY | D_CXX_COROUTINES)
/* The reserved keyword table. */
extern const struct c_common_resword c_common_reswords[];
else
cpp_define (pfile, "__cpp_concepts=201507L");
}
+ if (flag_coroutines)
+ cpp_define (pfile, "__cpp_coroutines=201902L"); /* n4835, C++20 CD */
if (flag_tm)
/* Use a value smaller than the 201505 specified in
the TS, since we don't yet support atomic_cancel. */
C++ ObjC++ Joined RejectNegative Host_Wide_Int Var(constexpr_ops_limit) Init(33554432)
-fconstexpr-ops-limit=<number> Specify maximum number of constexpr operations during a single constexpr evaluation.
+fcoroutines
+C++ LTO Var(flag_coroutines)
+Enable C++ coroutines (experimental).
+
fdebug-cpp
C ObjC C++ ObjC++
Emit debug annotations during preprocessing.
--- /dev/null
+/* This file contains the definitions and documentation for the
+ coroutines builtins used in GCC.
+
+ Copyright (C) 2018-2020 Free Software Foundation, Inc.
+
+ Contributed by Iain Sandoe <iain@sandoe.co.uk> under contract to Facebook.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+/* Before including this file, you should define a macro:
+
+ DEF_BUILTIN_STUB(ENUM, NAME)
+ DEF_COROUTINE_BUILTIN (ENUM, NAME, TYPE, ATTRS)
+
+ See builtins.def for details.
+ The builtins are created used by library implementations of C++
+ coroutines. */
+
+/* This has to come before all the coroutine builtins. */
+DEF_BUILTIN_STUB (BEGIN_COROUTINE_BUILTINS, (const char *) 0)
+
+/* These are the builtins that are externally-visible and used by the
+ standard library implementation of the coroutine header. */
+
+DEF_COROUTINE_BUILTIN (BUILT_IN_CORO_PROMISE, "promise",
+ BT_FN_PTR_PTR_CONST_SIZE_BOOL,
+ ATTR_CONST_NOTHROW_LEAF_LIST)
+
+DEF_COROUTINE_BUILTIN (BUILT_IN_CORO_RESUME, "resume", BT_FN_VOID_PTR,
+ ATTR_NULL)
+
+DEF_COROUTINE_BUILTIN (BUILT_IN_CORO_DESTROY, "destroy", BT_FN_VOID_PTR,
+ ATTR_NULL)
+
+DEF_COROUTINE_BUILTIN (BUILT_IN_CORO_DONE, "done", BT_FN_BOOL_PTR,
+ ATTR_NOTHROW_LEAF_LIST)
+
+/* This has to come after all the coroutine builtins. */
+DEF_BUILTIN_STUB (END_COROUTINE_BUILTINS, (const char *) 0)
--- /dev/null
+/* coroutine expansion and optimisation passes.
+
+ Copyright (C) 2018-2020 Free Software Foundation, Inc.
+
+ Contributed by Iain Sandoe <iain@sandoe.co.uk> under contract to Facebook.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "cgraph.h"
+#include "pretty-print.h"
+#include "diagnostic-core.h"
+#include "fold-const.h"
+#include "internal-fn.h"
+#include "langhooks.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "gimplify-me.h"
+#include "gimple-walk.h"
+#include "gimple-fold.h"
+#include "tree-cfg.h"
+#include "tree-into-ssa.h"
+#include "tree-ssa-propagate.h"
+#include "gimple-pretty-print.h"
+#include "cfghooks.h"
+
+/* Here we:
+ * lower the internal function that implements an exit from scope.
+ * expand the builtins that are used to implement the library
+ interfaces to the coroutine frame. */
+
+static tree
+lower_coro_builtin (gimple_stmt_iterator *gsi, bool *handled_ops_p,
+ struct walk_stmt_info *wi ATTRIBUTE_UNUSED)
+{
+ gimple *stmt = gsi_stmt (*gsi);
+ *handled_ops_p = !gimple_has_substatements (stmt);
+
+ if (gimple_code (stmt) != GIMPLE_CALL)
+ return NULL_TREE;
+
+ /* This internal function implements an exit from scope without
+ performing any cleanups; it jumps directly to the label provided. */
+ if (gimple_call_internal_p (stmt)
+ && gimple_call_internal_fn (stmt) == IFN_CO_SUSPN)
+ {
+ tree dest = TREE_OPERAND (gimple_call_arg (stmt, 0), 0);
+ ggoto *g = gimple_build_goto (dest);
+ gsi_replace (gsi, g, /* do EH */ false);
+ *handled_ops_p = true;
+ return NULL_TREE;
+ }
+
+ tree decl = gimple_call_fndecl (stmt);
+ if (!decl || !fndecl_built_in_p (decl, BUILT_IN_NORMAL))
+ return NULL_TREE;
+
+ /* The remaining builtins implement the library interfaces to the coro
+ frame. */
+ unsigned call_idx = 0;
+
+ switch (DECL_FUNCTION_CODE (decl))
+ {
+ default:
+ break;
+ case BUILT_IN_CORO_PROMISE:
+ {
+ /* If we are discarding this, then skip it; the function has no
+ side-effects. */
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs)
+ {
+ gsi_remove (gsi, true);
+ *handled_ops_p = true;
+ return NULL_TREE;
+ }
+ /* The coro frame starts with two pointers (to the resume and
+ destroy() functions). These are followed by the promise which
+ is aligned as per type [or user attribute].
+ The input pointer is the first argument.
+ The promise alignment is the second and the third is a bool
+ that is true when we are converting from a promise ptr to a
+ frame pointer, and false for the inverse. */
+ tree ptr = gimple_call_arg (stmt, 0);
+ tree align_t = gimple_call_arg (stmt, 1);
+ tree from = gimple_call_arg (stmt, 2);
+ gcc_checking_assert (TREE_CODE (align_t) == INTEGER_CST);
+ gcc_checking_assert (TREE_CODE (from) == INTEGER_CST);
+ bool dir = wi::to_wide (from) != 0;
+ HOST_WIDE_INT promise_align = TREE_INT_CST_LOW (align_t);
+ HOST_WIDE_INT psize =
+ TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node));
+ HOST_WIDE_INT align = TYPE_ALIGN_UNIT (ptr_type_node);
+ align = MAX (align, promise_align);
+ psize *= 2; /* Start with two pointers. */
+ psize = ROUND_UP (psize, align);
+ HOST_WIDE_INT offs = dir ? -psize : psize;
+ tree repl = build2 (POINTER_PLUS_EXPR, ptr_type_node, ptr,
+ size_int (offs));
+ gassign *grpl = gimple_build_assign (lhs, repl);
+ gsi_replace (gsi, grpl, true);
+ *handled_ops_p = true;
+ }
+ break;
+ case BUILT_IN_CORO_DESTROY:
+ call_idx = 1;
+ /* FALLTHROUGH */
+ case BUILT_IN_CORO_RESUME:
+ {
+ tree ptr = gimple_call_arg (stmt, 0); /* frame ptr. */
+ HOST_WIDE_INT psize =
+ TREE_INT_CST_LOW (TYPE_SIZE_UNIT (ptr_type_node));
+ HOST_WIDE_INT offset = call_idx * psize;
+ tree fntype = TREE_TYPE (decl);
+ tree fntype_ptr = build_pointer_type (fntype);
+ tree fntype_ppp = build_pointer_type (fntype_ptr);
+ tree indirect = fold_build2 (MEM_REF, fntype_ptr, ptr,
+ build_int_cst (fntype_ppp, offset));
+ tree f_ptr_tmp = make_ssa_name (TYPE_MAIN_VARIANT (fntype_ptr));
+ gassign *get_fptr = gimple_build_assign (f_ptr_tmp, indirect);
+ gsi_insert_before (gsi, get_fptr, GSI_SAME_STMT);
+ gimple_call_set_fn (static_cast<gcall *> (stmt), f_ptr_tmp);
+ *handled_ops_p = true;
+ }
+ break;
+ case BUILT_IN_CORO_DONE:
+ {
+ /* If we are discarding this, then skip it; the function has no
+ side-effects. */
+ tree lhs = gimple_call_lhs (stmt);
+ if (!lhs)
+ {
+ gsi_remove (gsi, true);
+ *handled_ops_p = true;
+ return NULL_TREE;
+ }
+ /* When we're done, the resume fn is set to NULL. */
+ tree ptr = gimple_call_arg (stmt, 0); /* frame ptr. */
+ tree vpp = build_pointer_type (ptr_type_node);
+ tree indirect
+ = fold_build2 (MEM_REF, vpp, ptr, build_int_cst (vpp, 0));
+ tree d_ptr_tmp = make_ssa_name (ptr_type_node);
+ gassign *get_dptr = gimple_build_assign (d_ptr_tmp, indirect);
+ gsi_insert_before (gsi, get_dptr, GSI_SAME_STMT);
+ tree done = fold_build2 (EQ_EXPR, boolean_type_node, d_ptr_tmp,
+ null_pointer_node);
+ gassign *get_res = gimple_build_assign (lhs, done);
+ gsi_replace (gsi, get_res, true);
+ *handled_ops_p = true;
+ }
+ break;
+ }
+ return NULL_TREE;
+}
+
+/* Main entry point for lowering coroutine FE builtins. */
+
+static unsigned int
+execute_lower_coro_builtins (void)
+{
+ struct walk_stmt_info wi;
+ gimple_seq body;
+
+ body = gimple_body (current_function_decl);
+ memset (&wi, 0, sizeof (wi));
+ walk_gimple_seq_mod (&body, lower_coro_builtin, NULL, &wi);
+ gimple_set_body (current_function_decl, body);
+
+ return 0;
+}
+
+namespace {
+
+const pass_data pass_data_coroutine_lower_builtins = {
+ GIMPLE_PASS, /* type */
+ "coro-lower-builtins", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0 /* todo_flags_finish */
+};
+
+class pass_coroutine_lower_builtins : public gimple_opt_pass
+{
+public:
+ pass_coroutine_lower_builtins (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_coroutine_lower_builtins, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate (function *) { return flag_coroutines; };
+
+ virtual unsigned int execute (function *f ATTRIBUTE_UNUSED)
+ {
+ return execute_lower_coro_builtins ();
+ }
+
+}; // class pass_coroutine_lower_builtins
+
+} // namespace
+
+gimple_opt_pass *
+make_pass_coroutine_lower_builtins (gcc::context *ctxt)
+{
+ return new pass_coroutine_lower_builtins (ctxt);
+}
+
+/* Expand the remaining coroutine IFNs.
+
+ In the front end we construct a single actor function that contains
+ the coroutine state machine.
+
+ The actor function has three entry conditions:
+ 1. from the ramp, resume point 0 - to initial-suspend.
+ 2. when resume () is executed (resume point N).
+ 3. from the destroy () shim when that is executed.
+
+ The actor function begins with two dispatchers; one for resume and
+ one for destroy (where the initial entry from the ramp is a special-
+ case of resume point 0).
+
+ Each suspend point and each dispatch entry is marked with an IFN such
+ that we can connect the relevant dispatchers to their target labels.
+
+ So, if we have:
+
+ CO_YIELD (NUM, FINAL, RES_LAB, DEST_LAB, FRAME_PTR)
+
+ This is await point NUM, and is the final await if FINAL is non-zero.
+ The resume point is RES_LAB, and the destroy point is DEST_LAB.
+
+ We expect to find a CO_ACTOR (NUM) in the resume dispatcher and a
+ CO_ACTOR (NUM+1) in the destroy dispatcher.
+
+ Initially, the intent of keeping the resume and destroy paths together
+ is that the conditionals controlling them are identical, and thus there
+ would be duplication of any optimisation of those paths if the split
+ were earlier.
+
+ Subsequent inlining of the actor (and DCE) is then able to extract the
+ resume and destroy paths as separate functions if that is found
+ profitable by the optimisers.
+
+ Once we have remade the connections to their correct postions, we elide
+ the labels that the front end inserted. */
+
+static void
+move_edge_and_update (edge e, basic_block old_bb, basic_block new_bb)
+{
+ if (dump_file)
+ fprintf (dump_file, "redirecting edge from bb %u to bb %u\n", old_bb->index,
+ new_bb->index);
+
+ e = redirect_edge_and_branch (e, new_bb);
+ if (!e && dump_file)
+ fprintf (dump_file, "failed to redirect edge .. \n");
+
+ /* Die if we failed. */
+ gcc_checking_assert (e);
+}
+
+static unsigned int
+execute_early_expand_coro_ifns (void)
+{
+ /* Don't rebuild stuff unless we have to. */
+ unsigned int todoflags = 0;
+ bool changed = false;
+ /* Some of the possible YIELD points will hopefully have been removed by
+ earlier optimisations; record the ones that are still present. */
+ hash_map<int_hash<HOST_WIDE_INT, -1, -2>, tree> destinations;
+ /* Labels we added to carry the CFG changes, we need to remove these to
+ avoid confusing EH. */
+ hash_set<tree> to_remove;
+ /* List of dispatch points to update. */
+ auto_vec<gimple_stmt_iterator, 16> actor_worklist;
+ basic_block bb;
+ gimple_stmt_iterator gsi;
+
+ FOR_EACH_BB_FN (bb, cfun)
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+ {
+ gimple *stmt = gsi_stmt (gsi);
+
+ if (!is_gimple_call (stmt) || !gimple_call_internal_p (stmt))
+ {
+ gsi_next (&gsi);
+ continue;
+ }
+ switch (gimple_call_internal_fn (stmt))
+ {
+ case IFN_CO_FRAME:
+ {
+ /* This internal function is a placeholder for the frame
+ size. In principle, we might lower it later (after some
+ optimisation had reduced the frame size). At present,
+ without any such optimisation, we just set it here. */
+ tree lhs = gimple_call_lhs (stmt);
+ tree size = gimple_call_arg (stmt, 0);
+ /* Right now, this is a trivial operation - copy through
+ the size computed during initial layout. */
+ gassign *grpl = gimple_build_assign (lhs, size);
+ gsi_replace (&gsi, grpl, true);
+ gsi_next (&gsi);
+ }
+ break;
+ case IFN_CO_ACTOR:
+ changed = true;
+ actor_worklist.safe_push (gsi); /* Save for later. */
+ gsi_next (&gsi);
+ break;
+ case IFN_CO_YIELD:
+ {
+ changed = true;
+ /* .CO_YIELD (NUM, FINAL, RES_LAB, DEST_LAB, FRAME_PTR);
+ NUM = await number.
+ FINAL = 1 if this is the final_suspend() await.
+ RES_LAB = resume point label.
+ DEST_LAB = destroy point label.
+ FRAME_PTR = is a null pointer with the type of the coro
+ frame, so that we can resize, if needed. */
+ if (dump_file)
+ fprintf (dump_file, "saw CO_YIELD in BB %u\n", bb->index);
+ tree num = gimple_call_arg (stmt, 0); /* yield point. */
+ HOST_WIDE_INT idx = TREE_INT_CST_LOW (num);
+ bool existed;
+ tree res_tgt = TREE_OPERAND (gimple_call_arg (stmt, 2), 0);
+ tree &res_dest = destinations.get_or_insert (idx, &existed);
+ if (existed && dump_file)
+ {
+ fprintf (
+ dump_file,
+ "duplicate YIELD RESUME point (" HOST_WIDE_INT_PRINT_DEC
+ ") ?\n",
+ idx);
+ print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
+ }
+ else
+ res_dest = res_tgt;
+ tree dst_tgt = TREE_OPERAND (gimple_call_arg (stmt, 3), 0);
+ tree &dst_dest = destinations.get_or_insert (idx + 1, &existed);
+ if (existed && dump_file)
+ {
+ fprintf (
+ dump_file,
+ "duplicate YIELD DESTROY point (" HOST_WIDE_INT_PRINT_DEC
+ ") ?\n",
+ idx + 1);
+ print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
+ }
+ else
+ dst_dest = dst_tgt;
+ to_remove.add (res_tgt);
+ to_remove.add (dst_tgt);
+ /* lose the co_yield. */
+ gsi_remove (&gsi, true);
+ stmt = gsi_stmt (gsi); /* next. */
+ /* lose the copy present at O0. */
+ if (is_gimple_assign (stmt))
+ {
+ gsi_remove (&gsi, true);
+ stmt = gsi_stmt (gsi);
+ }
+ /* Simplify the switch or if following. */
+ if (gswitch *gsw = dyn_cast<gswitch *> (stmt))
+ {
+ gimple_switch_set_index (gsw, integer_zero_node);
+ fold_stmt (&gsi);
+ }
+ else if (gcond *gif = dyn_cast<gcond *> (stmt))
+ {
+ if (gimple_cond_code (gif) == EQ_EXPR)
+ gimple_cond_make_true (gif);
+ else
+ gimple_cond_make_false (gif);
+ fold_stmt (&gsi);
+ }
+ else if (dump_file)
+ print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
+ if (gsi_end_p (gsi))
+ break;
+ continue;
+ }
+ default:
+ gsi_next (&gsi);
+ break;
+ }
+ }
+
+ if (!changed)
+ {
+ if (dump_file)
+ fprintf (dump_file, "coro: nothing to do\n");
+ return todoflags;
+ }
+
+ while (!actor_worklist.is_empty ())
+ {
+ gsi = actor_worklist.pop ();
+ gimple *stmt = gsi_stmt (gsi);
+ gcc_checking_assert (is_gimple_call (stmt)
+ && gimple_call_internal_p (stmt)
+ && gimple_call_internal_fn (stmt) == IFN_CO_ACTOR);
+ bb = gsi_bb (gsi);
+ HOST_WIDE_INT idx = TREE_INT_CST_LOW (gimple_call_arg (stmt, 0));
+ tree *seen = destinations.get (idx);
+ changed = true;
+
+ if (dump_file)
+ fprintf (dump_file, "saw CO_ACTOR in BB %u\n", bb->index);
+
+ if (!seen)
+ {
+ /* If we never saw this index, it means that the CO_YIELD
+ associated was elided during earlier optimisations, so we
+ don't need to fix up the switch targets. */
+ if (dump_file)
+ fprintf (dump_file, "yield point " HOST_WIDE_INT_PRINT_DEC
+ " not used, removing it .. \n", idx);
+ gsi_remove (&gsi, true);
+ release_defs (stmt);
+ }
+ else
+ {
+ /* So we need to switch the target of this switch case to the
+ relevant BB. */
+ basic_block new_bb = label_to_block (cfun, *seen);
+ /* We expect the block we're modifying to contain a single
+ CO_ACTOR() followed by a goto <switch default bb>. */
+ gcc_checking_assert (EDGE_COUNT (bb->succs) == 1);
+ edge e;
+ edge_iterator ei;
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ basic_block old_bb = e->dest;
+ move_edge_and_update (e, old_bb, new_bb);
+ }
+ gsi_remove (&gsi, true);
+ }
+ }
+
+ /* Remove the labels we inserted to map our hidden CFG, this
+ avoids confusing block merges when there are also EH labels. */
+ FOR_EACH_BB_FN (bb, cfun)
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+ {
+ gimple *stmt = gsi_stmt (gsi);
+ if (glabel *glab = dyn_cast<glabel *> (stmt))
+ {
+ tree rem = gimple_label_label (glab);
+ if (to_remove.contains (rem))
+ {
+ gsi_remove (&gsi, true);
+ to_remove.remove (rem);
+ continue; /* We already moved to the next insn. */
+ }
+ }
+ else
+ break;
+ gsi_next (&gsi);
+ }
+
+ /* Changed the CFG. */
+ todoflags |= TODO_cleanup_cfg;
+ return todoflags;
+}
+
+namespace {
+
+const pass_data pass_data_coroutine_early_expand_ifns = {
+ GIMPLE_PASS, /* type */
+ "coro-early-expand-ifns", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ (PROP_cfg), /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0 /* todo_flags_finish, set this in the fn. */
+};
+
+class pass_coroutine_early_expand_ifns : public gimple_opt_pass
+{
+public:
+ pass_coroutine_early_expand_ifns (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_coroutine_early_expand_ifns, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate (function *f)
+ {
+ return flag_coroutines && f->coroutine_component;
+ }
+
+ virtual unsigned int execute (function *f ATTRIBUTE_UNUSED)
+ {
+ return execute_early_expand_coro_ifns ();
+ }
+
+}; // class pass_coroutine_expand_ifns
+
+} // namespace
+
+gimple_opt_pass *
+make_pass_coroutine_early_expand_ifns (gcc::context *ctxt)
+{
+ return new pass_coroutine_early_expand_ifns (ctxt);
+}
+2020-01-18 Iain Sandoe <iain@sandoe.co.uk>
+
+ * Make-lang.in: Add coroutines.o.
+ * cp-tree.h (lang_decl-fn): coroutine_p, new bit.
+ (DECL_COROUTINE_P): New.
+ * lex.c (init_reswords): Enable keywords when the coroutine flag
+ is set,
+ * operators.def (co_await): New operator.
+ * call.c (add_builtin_candidates): Handle CO_AWAIT_EXPR.
+ (op_error): Likewise.
+ (build_new_op_1): Likewise.
+ (build_new_function_call): Validate coroutine builtin arguments.
+ * constexpr.c (potential_constant_expression_1): Handle
+ CO_AWAIT_EXPR, CO_YIELD_EXPR, CO_RETURN_EXPR.
+ * coroutines.cc: New file.
+ * cp-objcp-common.c (cp_common_init_ts): Add CO_AWAIT_EXPR,
+ CO_YIELD_EXPR, CO_RETRN_EXPR as TS expressions.
+ * cp-tree.def (CO_AWAIT_EXPR, CO_YIELD_EXPR, (CO_RETURN_EXPR): New.
+ * cp-tree.h (coro_validate_builtin_call): New.
+ * decl.c (emit_coro_helper): New.
+ (finish_function): Handle the case when a function is found to
+ be a coroutine, perform the outlining and emit the outlined
+ functions. Set a bit to signal that this is a coroutine component.
+ * parser.c (enum required_token): New enumeration RT_CO_YIELD.
+ (cp_parser_unary_expression): Handle co_await.
+ (cp_parser_assignment_expression): Handle co_yield.
+ (cp_parser_statement): Handle RID_CO_RETURN.
+ (cp_parser_jump_statement): Handle co_return.
+ (cp_parser_operator): Handle co_await operator.
+ (cp_parser_yield_expression): New.
+ (cp_parser_required_error): Handle RT_CO_YIELD.
+ * pt.c (tsubst_copy): Handle CO_AWAIT_EXPR.
+ (tsubst_expr): Handle CO_AWAIT_EXPR, CO_YIELD_EXPR and
+ CO_RETURN_EXPRs.
+ * tree.c (cp_walk_subtrees): Likewise.
+
2020-01-17 Jason Merrill <jason@redhat.com>
PR c++/92531 - ICE with noexcept(lambda).
# Language-specific object files for C++ and Objective C++.
CXX_AND_OBJCXX_OBJS = \
cp/call.o cp/class.o cp/constexpr.o cp/constraint.o \
- cp/cp-gimplify.o \
+ cp/coroutines.o cp/cp-gimplify.o \
cp/cp-objcp-common.o cp/cp-ubsan.o \
cp/cvt.o cp/cxx-pretty-print.o \
cp/decl.o cp/decl2.o cp/dump.o \
case ADDR_EXPR:
case COMPOUND_EXPR:
case COMPONENT_REF:
+ case CO_AWAIT_EXPR:
return;
case COND_EXPR:
result = build_over_call (cand, flags, complain);
}
+ if (flag_coroutines
+ && result
+ && TREE_CODE (result) == CALL_EXPR
+ && DECL_BUILT_IN_CLASS (TREE_OPERAND (CALL_EXPR_FN (result), 0))
+ == BUILT_IN_NORMAL)
+ result = coro_validate_builtin_call (result);
+
/* Free all the conversions we allocated. */
obstack_free (&conversion_obstack, p);
opname, opname, arg1, TREE_TYPE (arg1));
break;
+ case CO_AWAIT_EXPR:
+ if (flag_diagnostics_show_caret)
+ error_at (loc, op_error_string (G_("%<operator %s%>"), 1, match),
+ opname, TREE_TYPE (arg1));
+ else
+ error_at (loc, op_error_string (G_("%<operator %s%> in %<%s%E%>"),
+ 1, match),
+ opname, opname, arg1, TREE_TYPE (arg1));
+ break;
+
default:
if (arg2)
if (flag_diagnostics_show_caret)
case ADDR_EXPR:
case COMPOUND_EXPR:
case COMPONENT_REF:
+ case CO_AWAIT_EXPR:
result = NULL_TREE;
result_valid_p = true;
break;
case REALPART_EXPR:
case IMAGPART_EXPR:
case ABS_EXPR:
+ case CO_AWAIT_EXPR:
return cp_build_unary_op (code, arg1, false, complain);
case ARRAY_REF:
\$(srcdir)/c-family/c-common.c \$(srcdir)/c-family/c-format.c \
\$(srcdir)/c-family/c-cppbuiltin.c \$(srcdir)/c-family/c-pragma.c \
\$(srcdir)/cp/call.c \$(srcdir)/cp/class.c \$(srcdir)/cp/constexpr.c \
-\$(srcdir)/cp/constraint.cc \
+\$(srcdir)/cp/constraint.cc \$(srcdir)/cp/coroutines.cc \
\$(srcdir)/cp/cp-gimplify.c \
\$(srcdir)/cp/cp-lang.c \$(srcdir)/cp/cp-objcp-common.c \
\$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \
case ANNOTATE_EXPR:
return RECUR (TREE_OPERAND (t, 0), rval);
+ /* Coroutine await, yield and return expressions are not. */
+ case CO_AWAIT_EXPR:
+ case CO_YIELD_EXPR:
+ case CO_RETURN_EXPR:
+ return false;
+
default:
if (objc_is_property_ref (t))
return false;
--- /dev/null
+/* coroutine-specific state, expansions and tests.
+
+ Copyright (C) 2018-2020 Free Software Foundation, Inc.
+
+ Contributed by Iain Sandoe <iain@sandoe.co.uk> under contract to Facebook.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "cp-tree.h"
+#include "stringpool.h"
+#include "stmt.h"
+#include "stor-layout.h"
+#include "tree-iterator.h"
+#include "tree.h"
+#include "gcc-rich-location.h"
+#include "hash-map.h"
+
+static tree find_promise_type (tree);
+static bool coro_promise_type_found_p (tree, location_t);
+
+/* GCC C++ coroutines implementation.
+
+ The user authors a function that becomes a coroutine (lazily) by
+ making use of any of the co_await, co_yield or co_return keywords.
+
+ Unlike a regular function, where the activation record is placed on the
+ stack, and is destroyed on function exit, a coroutine has some state that
+ persists between calls - the coroutine frame (analogous to a stack frame).
+
+ We transform the user's function into three pieces:
+ 1. A so-called ramp function, that establishes the coroutine frame and
+ begins execution of the coroutine.
+ 2. An actor function that contains the state machine corresponding to the
+ user's suspend/resume structure.
+ 3. A stub function that calls the actor function in 'destroy' mode.
+
+ The actor function is executed:
+ * from "resume point 0" by the ramp.
+ * from resume point N ( > 0 ) for handle.resume() calls.
+ * from the destroy stub for destroy point N for handle.destroy() calls.
+
+ The functions in this file carry out the necessary analysis of, and
+ transforms to, the AST to perform this.
+
+ The C++ coroutine design makes use of some helper functions that are
+ authored in a so-called "promise" class provided by the user.
+
+ At parse time (or post substitution) the type of the coroutine promise
+ will be determined. At that point, we can look up the required promise
+ class methods and issue diagnostics if they are missing or incorrect. To
+ avoid repeating these actions at code-gen time, we make use of temporary
+ 'proxy' variables for the coroutine handle and the promise - which will
+ eventually be instantiated in the coroutine frame.
+
+ Each of the keywords will expand to a code sequence (although co_yield is
+ just syntactic sugar for a co_await).
+
+ We defer the analysis and transformation until template expansion is
+ complete so that we have complete types at that time. */
+
+
+/* The state that we collect during parsing (and template expansion) for
+ a coroutine. */
+
+struct GTY((for_user)) coroutine_info
+{
+ tree function_decl; /* The original function decl. */
+ tree promise_type; /* The cached promise type for this function. */
+ tree handle_type; /* The cached coroutine handle for this function. */
+ tree self_h_proxy; /* A handle instance that is used as the proxy for the
+ one that will eventually be allocated in the coroutine
+ frame. */
+ tree promise_proxy; /* Likewise, a proxy promise instance. */
+ location_t first_coro_keyword; /* The location of the keyword that made this
+ function into a coroutine. */
+};
+
+struct coroutine_info_hasher : ggc_ptr_hash<coroutine_info>
+{
+ typedef tree compare_type; /* We only compare the function decl. */
+ static inline hashval_t hash (coroutine_info *);
+ static inline hashval_t hash (const compare_type &);
+ static inline bool equal (coroutine_info *, coroutine_info *);
+ static inline bool equal (coroutine_info *, const compare_type &);
+};
+
+/* This table holds all the collected coroutine state for coroutines in
+ the current translation unit. */
+
+static GTY (()) hash_table<coroutine_info_hasher> *coroutine_info_table;
+
+/* We will initialise state lazily. */
+static bool coro_initialized = false;
+
+/* Return a hash value for the entry pointed to by INFO.
+ The compare type is a tree, but the only trees we are going use are
+ function decls. We use the DECL_UID as the hash value since that is
+ stable across PCH. */
+
+hashval_t
+coroutine_info_hasher::hash (coroutine_info *info)
+{
+ return DECL_UID (info->function_decl);
+}
+
+/* Return a hash value for the compare value COMP. */
+
+hashval_t
+coroutine_info_hasher::hash (const compare_type& comp)
+{
+ return DECL_UID (comp);
+}
+
+/* Return true if the entries pointed to by LHS and RHS are for the
+ same coroutine. */
+
+bool
+coroutine_info_hasher::equal (coroutine_info *lhs, coroutine_info *rhs)
+{
+ return lhs->function_decl == rhs->function_decl;
+}
+
+bool
+coroutine_info_hasher::equal (coroutine_info *lhs, const compare_type& rhs)
+{
+ return lhs->function_decl == rhs;
+}
+
+/* Get the existing coroutine_info for FN_DECL, or insert a new one if the
+ entry does not yet exist. */
+
+coroutine_info *
+get_or_insert_coroutine_info (tree fn_decl)
+{
+ gcc_checking_assert (coroutine_info_table != NULL);
+
+ coroutine_info **slot = coroutine_info_table->find_slot_with_hash
+ (fn_decl, coroutine_info_hasher::hash (fn_decl), INSERT);
+
+ if (*slot == NULL)
+ {
+ *slot = new (ggc_cleared_alloc<coroutine_info> ()) coroutine_info ();
+ (*slot)->function_decl = fn_decl;
+ }
+
+ return *slot;
+}
+
+/* Get the existing coroutine_info for FN_DECL, fail if it doesn't exist. */
+
+coroutine_info *
+get_coroutine_info (tree fn_decl)
+{
+ gcc_checking_assert (coroutine_info_table != NULL);
+
+ coroutine_info **slot = coroutine_info_table->find_slot_with_hash
+ (fn_decl, coroutine_info_hasher::hash (fn_decl), NO_INSERT);
+ if (slot)
+ return *slot;
+ return NULL;
+}
+
+/* We will lazily create all the identifiers that are used by coroutines
+ on the first attempt to lookup the traits. */
+
+/* Identifiers that are used by all coroutines. */
+
+static GTY(()) tree coro_traits_identifier;
+static GTY(()) tree coro_handle_identifier;
+static GTY(()) tree coro_promise_type_identifier;
+
+/* Required promise method name identifiers. */
+
+static GTY(()) tree coro_await_transform_identifier;
+static GTY(()) tree coro_initial_suspend_identifier;
+static GTY(()) tree coro_final_suspend_identifier;
+static GTY(()) tree coro_return_void_identifier;
+static GTY(()) tree coro_return_value_identifier;
+static GTY(()) tree coro_yield_value_identifier;
+static GTY(()) tree coro_resume_identifier;
+static GTY(()) tree coro_from_address_identifier;
+static GTY(()) tree coro_get_return_object_identifier;
+static GTY(()) tree coro_gro_on_allocation_fail_identifier;
+static GTY(()) tree coro_unhandled_exception_identifier;
+
+/* Awaitable methods. */
+
+static GTY(()) tree coro_await_ready_identifier;
+static GTY(()) tree coro_await_suspend_identifier;
+static GTY(()) tree coro_await_resume_identifier;
+
+/* Create the identifiers used by the coroutines library interfaces. */
+
+static void
+coro_init_identifiers ()
+{
+ coro_traits_identifier = get_identifier ("coroutine_traits");
+ coro_handle_identifier = get_identifier ("coroutine_handle");
+ coro_promise_type_identifier = get_identifier ("promise_type");
+
+ coro_await_transform_identifier = get_identifier ("await_transform");
+ coro_initial_suspend_identifier = get_identifier ("initial_suspend");
+ coro_final_suspend_identifier = get_identifier ("final_suspend");
+ coro_return_void_identifier = get_identifier ("return_void");
+ coro_return_value_identifier = get_identifier ("return_value");
+ coro_yield_value_identifier = get_identifier ("yield_value");
+ coro_resume_identifier = get_identifier ("resume");
+ coro_from_address_identifier = get_identifier ("from_address");
+ coro_get_return_object_identifier = get_identifier ("get_return_object");
+ coro_gro_on_allocation_fail_identifier =
+ get_identifier ("get_return_object_on_allocation_failure");
+ coro_unhandled_exception_identifier = get_identifier ("unhandled_exception");
+
+ coro_await_ready_identifier = get_identifier ("await_ready");
+ coro_await_suspend_identifier = get_identifier ("await_suspend");
+ coro_await_resume_identifier = get_identifier ("await_resume");
+}
+
+/* Trees we only need to set up once. */
+
+static GTY(()) tree coro_traits_templ;
+static GTY(()) tree coro_handle_templ;
+static GTY(()) tree void_coro_handle_type;
+
+/* ================= Parse, Semantics and Type checking ================= */
+
+/* This initial set of routines are helper for the parsing and template
+ expansion phases.
+
+ At the completion of this, we will have completed trees for each of the
+ keywords, but making use of proxy variables for the self-handle and the
+ promise class instance. */
+
+/* [coroutine.traits]
+ Lookup the coroutine_traits template decl. */
+
+static tree
+find_coro_traits_template_decl (location_t kw)
+{
+ tree traits_decl = lookup_qualified_name (std_node, coro_traits_identifier,
+ 0, true);
+ if (traits_decl == NULL_TREE || traits_decl == error_mark_node)
+ {
+ error_at (kw, "cannot find %<coroutine traits%> template");
+ return NULL_TREE;
+ }
+ else
+ return traits_decl;
+}
+
+/* Instantiate Coroutine traits for the function signature. */
+
+static tree
+instantiate_coro_traits (tree fndecl, location_t kw)
+{
+ /* [coroutine.traits.primary]
+ So now build up a type list for the template <typename _R, typename...>.
+ The types are the function's arg types and _R is the function return
+ type. */
+
+ tree functyp = TREE_TYPE (fndecl);
+ tree arg_node = TYPE_ARG_TYPES (functyp);
+ tree argtypes = make_tree_vec (list_length (arg_node)-1);
+ unsigned p = 0;
+
+ while (arg_node != NULL_TREE && !VOID_TYPE_P (TREE_VALUE (arg_node)))
+ {
+ TREE_VEC_ELT (argtypes, p++) = TREE_VALUE (arg_node);
+ arg_node = TREE_CHAIN (arg_node);
+ }
+
+ tree argtypepack = cxx_make_type (TYPE_ARGUMENT_PACK);
+ SET_ARGUMENT_PACK_ARGS (argtypepack, argtypes);
+
+ tree targ = make_tree_vec (2);
+ TREE_VEC_ELT (targ, 0) = TREE_TYPE (functyp);
+ TREE_VEC_ELT (targ, 1) = argtypepack;
+
+ tree traits_class
+ = lookup_template_class (coro_traits_templ, targ,
+ /*in_decl=*/ NULL_TREE,
+ /*context=*/ NULL_TREE /*std_node*/,
+ /*entering scope=*/ false, tf_warning_or_error);
+
+ if (traits_class == error_mark_node || traits_class == NULL_TREE)
+ {
+ error_at (kw, "cannot instantiate %<coroutine traits%>");
+ return NULL_TREE;
+ }
+
+ return traits_class;
+}
+
+/* [coroutine.handle] */
+
+static tree
+find_coro_handle_template_decl (location_t kw)
+{
+ tree handle_decl = lookup_qualified_name (std_node, coro_handle_identifier,
+ 0, true);
+ if (handle_decl == NULL_TREE || handle_decl == error_mark_node)
+ {
+ error_at (kw, "cannot find %<coroutine handle%> template");
+ return NULL_TREE;
+ }
+ else
+ return handle_decl;
+}
+
+/* Instantiate the handle template for a given promise type. */
+
+static tree
+instantiate_coro_handle_for_promise_type (location_t kw, tree promise_type)
+{
+ /* So now build up a type list for the template, one entry, the promise. */
+ tree targ = make_tree_vec (1);
+ TREE_VEC_ELT (targ, 0) = promise_type;
+ tree handle_type
+ = lookup_template_class (coro_handle_identifier, targ,
+ /* in_decl */ NULL_TREE,
+ /* context */ std_node,
+ /* entering scope */ false, tf_warning_or_error);
+
+ if (handle_type == error_mark_node)
+ {
+ error_at (kw, "cannot instantiate a %<coroutine handle%> for"
+ " promise type %qT", promise_type);
+ return NULL_TREE;
+ }
+
+ return handle_type;
+}
+
+/* Look for the promise_type in the instantiated traits. */
+
+static tree
+find_promise_type (tree traits_class)
+{
+ tree promise_type
+ = lookup_member (traits_class, coro_promise_type_identifier,
+ /* protect */ 1, /*want_type=*/true, tf_warning_or_error);
+
+ if (promise_type)
+ promise_type
+ = complete_type_or_else (TREE_TYPE (promise_type), promise_type);
+
+ /* NULL_TREE on fail. */
+ return promise_type;
+}
+
+static bool
+coro_promise_type_found_p (tree fndecl, location_t loc)
+{
+ gcc_assert (fndecl != NULL_TREE);
+
+ /* Save the coroutine data on the side to avoid the overhead on every
+ function decl. */
+
+ /* We only need one entry per coroutine in a TU, the assumption here is that
+ there are typically not 1000s. */
+ if (!coro_initialized)
+ {
+ gcc_checking_assert (coroutine_info_table == NULL);
+ /* A table to hold the state, per coroutine decl. */
+ coroutine_info_table =
+ hash_table<coroutine_info_hasher>::create_ggc (11);
+ /* Set up the identifiers we will use. */
+ gcc_checking_assert (coro_traits_identifier == NULL);
+ coro_init_identifiers ();
+ /* Trees we only need to create once. */
+ /* Coroutine traits template. */
+ coro_traits_templ = find_coro_traits_template_decl (loc);
+ gcc_checking_assert (coro_traits_templ != NULL);
+ /* coroutine_handle<> template. */
+ coro_handle_templ = find_coro_handle_template_decl (loc);
+ gcc_checking_assert (coro_handle_templ != NULL);
+ /* We can also instantiate the void coroutine_handle<> */
+ void_coro_handle_type =
+ instantiate_coro_handle_for_promise_type (loc, NULL_TREE);
+ gcc_checking_assert (void_coro_handle_type != NULL);
+ coro_initialized = true;
+ }
+
+ coroutine_info *coro_info = get_or_insert_coroutine_info (fndecl);
+ /* Without this, we cannot really proceed. */
+ gcc_checking_assert (coro_info);
+
+ /* If we don't already have a current promise type, try to look it up. */
+ if (coro_info->promise_type == NULL_TREE)
+ {
+ /* Get the coroutine traits template class instance for the function
+ signature we have - coroutine_traits <R, ...> */
+ tree templ_class = instantiate_coro_traits (fndecl, loc);
+
+ /* Find the promise type for that. */
+ coro_info->promise_type = find_promise_type (templ_class);
+
+ /* If we don't find it, punt on the rest. */
+ if (coro_info->promise_type == NULL_TREE)
+ {
+ error_at (loc, "unable to find the promise type for this coroutine");
+ return false;
+ }
+
+ /* Try to find the handle type for the promise. */
+ tree handle_type =
+ instantiate_coro_handle_for_promise_type (loc, coro_info->promise_type);
+ if (handle_type == NULL_TREE)
+ return false;
+
+ /* Complete this, we're going to use it. */
+ coro_info->handle_type = complete_type_or_else (handle_type, fndecl);
+ /* Diagnostic would be emitted by complete_type_or_else. */
+ if (coro_info->handle_type == error_mark_node)
+ return false;
+
+ /* Build a proxy for a handle to "self" as the param to
+ await_suspend() calls. */
+ coro_info->self_h_proxy
+ = build_lang_decl (VAR_DECL, get_identifier ("self_h.proxy"),
+ coro_info->handle_type);
+
+ /* Build a proxy for the promise so that we can perform lookups. */
+ coro_info->promise_proxy
+ = build_lang_decl (VAR_DECL, get_identifier ("promise.proxy"),
+ coro_info->promise_type);
+
+ /* Note where we first saw a coroutine keyword. */
+ coro_info->first_coro_keyword = loc;
+ }
+
+ return true;
+}
+
+/* These functions assumes that the caller has verified that the state for
+ the decl has been initialized, we try to minimize work here. */
+
+static tree
+get_coroutine_promise_type (tree decl)
+{
+ if (coroutine_info *info = get_coroutine_info (decl))
+ return info->promise_type;
+
+ return NULL_TREE;
+}
+
+static tree
+get_coroutine_handle_type (tree decl)
+{
+ if (coroutine_info *info = get_coroutine_info (decl))
+ return info->handle_type;
+
+ return NULL_TREE;
+}
+
+static tree
+get_coroutine_self_handle_proxy (tree decl)
+{
+ if (coroutine_info *info = get_coroutine_info (decl))
+ return info->self_h_proxy;
+
+ return NULL_TREE;
+}
+
+static tree
+get_coroutine_promise_proxy (tree decl)
+{
+ if (coroutine_info *info = get_coroutine_info (decl))
+ return info->promise_proxy;
+
+ return NULL_TREE;
+}
+
+static tree
+lookup_promise_method (tree fndecl, tree member_id, location_t loc,
+ bool musthave)
+{
+ tree promise = get_coroutine_promise_type (fndecl);
+ tree pm_memb
+ = lookup_member (promise, member_id,
+ /*protect*/ 1, /*want_type*/ 0, tf_warning_or_error);
+ if (musthave && (pm_memb == NULL_TREE || pm_memb == error_mark_node))
+ {
+ error_at (loc, "no member named %qE in %qT", member_id, promise);
+ return error_mark_node;
+ }
+ return pm_memb;
+}
+
+/* Here we check the constraints that are common to all keywords (since the
+ presence of a coroutine keyword makes the function into a coroutine). */
+
+static bool
+coro_common_keyword_context_valid_p (tree fndecl, location_t kw_loc,
+ const char *kw_name)
+{
+ if (fndecl == NULL_TREE)
+ {
+ error_at (kw_loc, "%qs cannot be used outside a function", kw_name);
+ return false;
+ }
+
+ /* This is arranged in order of prohibitions in the std. */
+ if (DECL_MAIN_P (fndecl))
+ {
+ // [basic.start.main] 3. The function main shall not be a coroutine.
+ error_at (kw_loc, "%qs cannot be used in the %<main%> function",
+ kw_name);
+ return false;
+ }
+
+ if (DECL_DECLARED_CONSTEXPR_P (fndecl))
+ {
+ // [dcl.constexpr] 3.3 it shall not be a coroutine.
+ error_at (kw_loc, "%qs cannot be used in a %<constexpr%> function",
+ kw_name);
+ cp_function_chain->invalid_constexpr = true;
+ return false;
+ }
+
+ if (FNDECL_USED_AUTO (fndecl))
+ {
+ // [dcl.spec.auto] 15. A function declared with a return type that uses
+ // a placeholder type shall not be a coroutine .
+ error_at (kw_loc,
+ "%qs cannot be used in a function with a deduced return type",
+ kw_name);
+ return false;
+ }
+
+ if (varargs_function_p (fndecl))
+ {
+ // [dcl.fct.def.coroutine] The parameter-declaration-clause of the
+ // coroutine shall not terminate with an ellipsis that is not part
+ // of a parameter-declaration.
+ error_at (kw_loc,
+ "%qs cannot be used in a varargs function", kw_name);
+ return false;
+ }
+
+ if (DECL_CONSTRUCTOR_P (fndecl))
+ {
+ // [class.ctor] 7. a constructor shall not be a coroutine.
+ error_at (kw_loc, "%qs cannot be used in a constructor", kw_name);
+ return false;
+ }
+
+ if (DECL_DESTRUCTOR_P (fndecl))
+ {
+ // [class.dtor] 21. a destructor shall not be a coroutine.
+ error_at (kw_loc, "%qs cannot be used in a destructor", kw_name);
+ return false;
+ }
+
+ return true;
+}
+
+/* Here we check the constraints that are not per keyword. */
+
+static bool
+coro_function_valid_p (tree fndecl)
+{
+ location_t f_loc = DECL_SOURCE_LOCATION (fndecl);
+
+ /* Since we think the function is a coroutine, that implies we parsed
+ a keyword that triggered this. Keywords check promise validity for
+ their context and thus the promise type should be known at this point. */
+ gcc_checking_assert (get_coroutine_handle_type (fndecl) != NULL_TREE
+ && get_coroutine_promise_type (fndecl) != NULL_TREE);
+
+ if (current_function_returns_value || current_function_returns_null)
+ {
+ /* TODO: record or extract positions of returns (and the first coro
+ keyword) so that we can add notes to the diagnostic about where
+ the bad keyword is and what made the function into a coro. */
+ error_at (f_loc, "a %<return%> statement is not allowed in coroutine;"
+ " did you mean %<co_return%>?");
+ return false;
+ }
+
+ return true;
+}
+
+enum suspend_point_kind {
+ CO_AWAIT_SUSPEND_POINT = 0,
+ CO_YIELD_SUSPEND_POINT,
+ INITIAL_SUSPEND_POINT,
+ FINAL_SUSPEND_POINT
+};
+
+/* This performs [expr.await] bullet 3.3 and validates the interface obtained.
+ It is also used to build the initial and final suspend points.
+
+ 'a', 'o' and 'e' are used as per the description in the section noted.
+
+ A, the original yield/await expr, is found at source location LOC.
+
+ We will be constructing a CO_AWAIT_EXPR for a suspend point of one of
+ the four suspend_point_kind kinds. This is indicated by SUSPEND_KIND. */
+
+static tree
+build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind)
+{
+ /* Try and overload of operator co_await, .... */
+ tree o;
+ if (MAYBE_CLASS_TYPE_P (TREE_TYPE (a)))
+ {
+ tree overload = NULL_TREE;
+ o = build_new_op (loc, CO_AWAIT_EXPR, LOOKUP_NORMAL, a, NULL_TREE,
+ NULL_TREE, &overload, tf_warning_or_error);
+ /* If no viable functions are found, o is a. */
+ if (!o || o == error_mark_node)
+ o = a;
+ }
+ else
+ o = a; /* This is most likely about to fail anyway. */
+
+ tree o_type = complete_type_or_else (TREE_TYPE (o), o);
+ if (TREE_CODE (o_type) != RECORD_TYPE)
+ {
+ error_at (loc, "awaitable type %qT is not a structure",
+ o_type);
+ return error_mark_node;
+ }
+
+ /* Check for required awaitable members and their types. */
+ tree awrd_meth
+ = lookup_member (o_type, coro_await_ready_identifier,
+ /* protect */ 1, /*want_type=*/0, tf_warning_or_error);
+
+ if (!awrd_meth || awrd_meth == error_mark_node)
+ return error_mark_node;
+
+ tree awsp_meth
+ = lookup_member (o_type, coro_await_suspend_identifier,
+ /* protect */ 1, /*want_type=*/0, tf_warning_or_error);
+
+ if (!awsp_meth || awsp_meth == error_mark_node)
+ return error_mark_node;
+
+ /* The type of the co_await is the return type of the awaitable's
+ co_resume(), so we need to look that up. */
+ tree awrs_meth
+ = lookup_member (o_type, coro_await_resume_identifier,
+ /* protect */ 1, /*want_type=*/0, tf_warning_or_error);
+
+ if (!awrs_meth || awrs_meth == error_mark_node)
+ return error_mark_node;
+
+ /* To complete the lookups, we need an instance of 'e' which is built from
+ 'o' according to [expr.await] 3.4. However, we don't want to materialize
+ 'e' here (it might need to be placed in the coroutine frame) so we will
+ make a temp placeholder instead. */
+ tree e_proxy = build_lang_decl (VAR_DECL, NULL_TREE, o_type);
+
+ /* I suppose we could check that this is contextually convertible to bool. */
+ tree awrd_func = NULL_TREE;
+ tree awrd_call
+ = build_new_method_call (e_proxy, awrd_meth, NULL, NULL_TREE, LOOKUP_NORMAL,
+ &awrd_func, tf_warning_or_error);
+
+ if (!awrd_func || !awrd_call || awrd_call == error_mark_node)
+ return error_mark_node;
+
+ /* The suspend method may return one of three types:
+ 1. void (no special action needed).
+ 2. bool (if true, we don't need to suspend).
+ 3. a coroutine handle, we execute the handle.resume() call. */
+ tree awsp_func = NULL_TREE;
+ tree h_proxy = get_coroutine_self_handle_proxy (current_function_decl);
+ vec<tree, va_gc> *args = make_tree_vector_single (h_proxy);
+ tree awsp_call
+ = build_new_method_call (e_proxy, awsp_meth, &args, NULL_TREE,
+ LOOKUP_NORMAL, &awsp_func, tf_warning_or_error);
+
+ release_tree_vector (args);
+ if (!awsp_func || !awsp_call || awsp_call == error_mark_node)
+ return error_mark_node;
+
+ bool ok = false;
+ tree susp_return_type = TREE_TYPE (TREE_TYPE (awsp_func));
+ if (same_type_p (susp_return_type, void_type_node))
+ ok = true;
+ else if (same_type_p (susp_return_type, boolean_type_node))
+ ok = true;
+ else if (TREE_CODE (susp_return_type) == RECORD_TYPE
+ && CLASS_TYPE_P (susp_return_type))
+ {
+ tree tt = CLASSTYPE_TI_TEMPLATE (susp_return_type);
+ if (tt == coro_handle_templ)
+ ok = true;
+ }
+
+ if (!ok)
+ {
+ error_at (loc, "%<await_suspend%> must return %<void%>, %<bool%> or"
+ " a coroutine handle");
+ return error_mark_node;
+ }
+
+ /* Finally, the type of e.await_resume() is the co_await's type. */
+ tree awrs_func = NULL_TREE;
+ tree awrs_call
+ = build_new_method_call (e_proxy, awrs_meth, NULL, NULL_TREE, LOOKUP_NORMAL,
+ &awrs_func, tf_warning_or_error);
+
+ if (!awrs_func || !awrs_call || awrs_call == error_mark_node)
+ return error_mark_node;
+
+ /* We now have three call expressions, in terms of the promise, handle and
+ 'e' proxies. Save them in the await expression for later expansion. */
+
+ tree awaiter_calls = make_tree_vec (3);
+ TREE_VEC_ELT (awaiter_calls, 0) = awrd_call; /* await_ready(). */
+ TREE_VEC_ELT (awaiter_calls, 1) = awsp_call; /* await_suspend(). */
+ TREE_VEC_ELT (awaiter_calls, 2) = awrs_call; /* await_resume(). */
+
+ return build5_loc (loc, CO_AWAIT_EXPR, TREE_TYPE (awrs_call), a,
+ e_proxy, o, awaiter_calls,
+ build_int_cst (integer_type_node, (int) suspend_kind));
+}
+
+tree
+finish_co_await_expr (location_t kw, tree expr)
+{
+ if (!expr || error_operand_p (expr))
+ return error_mark_node;
+
+ if (!coro_common_keyword_context_valid_p (current_function_decl, kw,
+ "co_await"))
+ return error_mark_node;
+
+ /* The current function has now become a coroutine, if it wasn't already. */
+ DECL_COROUTINE_P (current_function_decl) = 1;
+
+ if (processing_template_decl)
+ {
+ if (check_for_bare_parameter_packs (expr))
+ return error_mark_node;
+
+ /* If we don't know the promise type, we can't proceed. */
+ tree functype = TREE_TYPE (current_function_decl);
+ if (dependent_type_p (functype) || type_dependent_expression_p (expr))
+ return build5_loc (kw, CO_AWAIT_EXPR, TREE_TYPE (expr), expr, NULL_TREE,
+ NULL_TREE, NULL_TREE, integer_zero_node);
+ }
+
+ /* We must be able to look up the "await_transform" method in the scope of
+ the promise type, and obtain its return type. */
+ if (!coro_promise_type_found_p (current_function_decl, kw))
+ return error_mark_node;
+
+ /* [expr.await] 3.2
+ The incoming cast expression might be transformed by a promise
+ 'await_transform()'. */
+ tree at_meth
+ = lookup_promise_method (current_function_decl,
+ coro_await_transform_identifier, kw,
+ /*musthave=*/ false);
+ if (at_meth == error_mark_node)
+ return error_mark_node;
+
+ tree a = expr;
+ if (at_meth)
+ {
+ /* try to build a = p.await_transform (e). */
+ tree at_fn = NULL_TREE;
+ vec<tree, va_gc> *args = make_tree_vector_single (expr);
+ a = build_new_method_call (get_coroutine_promise_proxy (
+ current_function_decl),
+ at_meth, &args, NULL_TREE, LOOKUP_NORMAL,
+ &at_fn, tf_warning_or_error);
+
+ /* As I read the section.
+ We saw an await_transform method, so it's mandatory that we replace
+ expr with p.await_transform (expr), therefore if the method call fails
+ (presumably, we don't have suitable arguments) then this part of the
+ process fails. */
+ if (!at_fn || a == error_mark_node)
+ return error_mark_node;
+ }
+
+ /* Now we want to build co_await a. */
+ tree op = build_co_await (kw, a, CO_AWAIT_SUSPEND_POINT);
+ TREE_SIDE_EFFECTS (op) = 1;
+ SET_EXPR_LOCATION (op, kw);
+
+ return op;
+}
+
+/* Take the EXPR given and attempt to build:
+ co_await p.yield_value (expr);
+ per [expr.yield] para 1. */
+
+tree
+finish_co_yield_expr (location_t kw, tree expr)
+{
+ if (!expr || error_operand_p (expr))
+ return error_mark_node;
+
+ /* Check the general requirements and simple syntax errors. */
+ if (!coro_common_keyword_context_valid_p (current_function_decl, kw,
+ "co_yield"))
+ return error_mark_node;
+
+ /* The current function has now become a coroutine, if it wasn't already. */
+ DECL_COROUTINE_P (current_function_decl) = 1;
+
+ if (processing_template_decl)
+ {
+ if (check_for_bare_parameter_packs (expr))
+ return error_mark_node;
+
+ tree functype = TREE_TYPE (current_function_decl);
+ /* If we don't know the promise type, we can't proceed. */
+ if (dependent_type_p (functype) || type_dependent_expression_p (expr))
+ return build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (expr), expr,
+ NULL_TREE);
+ }
+
+ if (!coro_promise_type_found_p (current_function_decl, kw))
+ /* We must be able to look up the "yield_value" method in the scope of
+ the promise type, and obtain its return type. */
+ return error_mark_node;
+
+ /* The incoming expr is "e" per [expr.yield] para 1, lookup and build a
+ call for p.yield_value(e). */
+ tree y_meth = lookup_promise_method (current_function_decl,
+ coro_yield_value_identifier, kw,
+ /*musthave=*/ true);
+ if (!y_meth || y_meth == error_mark_node)
+ return error_mark_node;
+
+ tree yield_fn = NULL_TREE;
+ vec<tree, va_gc> *args = make_tree_vector_single (expr);
+ tree yield_call = build_new_method_call (
+ get_coroutine_promise_proxy (current_function_decl), y_meth, &args,
+ NULL_TREE, LOOKUP_NORMAL, &yield_fn, tf_warning_or_error);
+
+ if (!yield_fn || yield_call == error_mark_node)
+ return error_mark_node;
+
+ /* So now we have the type of p.yield_value (e).
+ Now we want to build co_await p.yield_value (e).
+ Noting that for co_yield, there is no evaluation of any potential
+ promise transform_await(). */
+
+ tree op = build_co_await (kw, yield_call, CO_YIELD_SUSPEND_POINT);
+
+ op = build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (op), expr, op);
+ TREE_SIDE_EFFECTS (op) = 1;
+
+ return op;
+}
+
+/* Check that it's valid to have a co_return keyword here.
+ If it is, then check and build the p.return_{void(),value(expr)}.
+ These are built against the promise proxy, but saved for expand time. */
+
+tree
+finish_co_return_stmt (location_t kw, tree expr)
+{
+ if (expr == error_mark_node)
+ return error_mark_node;
+
+ if (!coro_common_keyword_context_valid_p (current_function_decl, kw,
+ "co_return"))
+ return error_mark_node;
+
+ /* The current function has now become a coroutine, if it wasn't
+ already. */
+ DECL_COROUTINE_P (current_function_decl) = 1;
+
+ if (processing_template_decl)
+ {
+ current_function_returns_value = 1;
+
+ if (check_for_bare_parameter_packs (expr))
+ return error_mark_node;
+
+ tree functype = TREE_TYPE (current_function_decl);
+ /* If we don't know the promise type, we can't proceed, return the
+ expression as it is. */
+ if (dependent_type_p (functype) || type_dependent_expression_p (expr))
+ {
+ expr
+ = build2_loc (kw, CO_RETURN_EXPR, void_type_node, expr, NULL_TREE);
+ expr = maybe_cleanup_point_expr_void (expr);
+ expr = add_stmt (expr);
+ return expr;
+ }
+ }
+
+ if (!coro_promise_type_found_p (current_function_decl, kw))
+ return error_mark_node;
+
+ if (error_operand_p (expr))
+ return error_mark_node;
+
+ /* Suppress -Wreturn-type for co_return, we need to check indirectly
+ whether the promise type has a suitable return_void/return_value. */
+ TREE_NO_WARNING (current_function_decl) = true;
+
+ if (!processing_template_decl && warn_sequence_point)
+ verify_sequence_points (expr);
+
+ /* If the promise object doesn't have the correct return call then
+ there's a mis-match between the co_return <expr> and this. */
+ tree co_ret_call = NULL_TREE;
+ if (expr == NULL_TREE || VOID_TYPE_P (TREE_TYPE (expr)))
+ {
+ tree crv_meth
+ = lookup_promise_method (current_function_decl,
+ coro_return_void_identifier, kw,
+ /*musthave=*/ true);
+ if (!crv_meth || crv_meth == error_mark_node)
+ return error_mark_node;
+
+ co_ret_call = build_new_method_call (
+ get_coroutine_promise_proxy (current_function_decl), crv_meth, NULL,
+ NULL_TREE, LOOKUP_NORMAL, NULL, tf_warning_or_error);
+ }
+ else
+ {
+ tree crv_meth
+ = lookup_promise_method (current_function_decl,
+ coro_return_value_identifier, kw,
+ /*musthave=*/ true);
+ if (!crv_meth || crv_meth == error_mark_node)
+ return error_mark_node;
+
+ vec<tree, va_gc> *args = make_tree_vector_single (expr);
+ co_ret_call = build_new_method_call (
+ get_coroutine_promise_proxy (current_function_decl), crv_meth, &args,
+ NULL_TREE, LOOKUP_NORMAL, NULL, tf_warning_or_error);
+ }
+
+ /* Makes no sense for a co-routine really. */
+ if (TREE_THIS_VOLATILE (current_function_decl))
+ warning_at (kw, 0,
+ "function declared %<noreturn%> has a"
+ " %<co_return%> statement");
+
+ if (!co_ret_call || co_ret_call == error_mark_node)
+ return error_mark_node;
+
+ expr = build2_loc (kw, CO_RETURN_EXPR, void_type_node, expr, co_ret_call);
+ expr = maybe_cleanup_point_expr_void (expr);
+ expr = add_stmt (expr);
+ return expr;
+}
+
+/* We need to validate the arguments to __builtin_coro_promise, since the
+ second two must be constant, and the builtins machinery doesn't seem to
+ deal with that properly. */
+
+tree
+coro_validate_builtin_call (tree call, tsubst_flags_t)
+{
+ tree fn = TREE_OPERAND (CALL_EXPR_FN (call), 0);
+
+ gcc_checking_assert (DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL);
+ switch (DECL_FUNCTION_CODE (fn))
+ {
+ default:
+ return call;
+
+ case BUILT_IN_CORO_PROMISE:
+ {
+ /* Argument 0 is already checked by the normal built-in machinery
+ Argument 1 must be a constant of size type. It probably makes
+ little sense if it's not a power of 2, but that isn't specified
+ formally. */
+ tree arg = CALL_EXPR_ARG (call, 1);
+ location_t loc = EXPR_LOCATION (arg);
+
+ /* We expect alignof expressions in templates. */
+ if (TREE_CODE (arg) == NON_DEPENDENT_EXPR
+ && TREE_CODE (TREE_OPERAND (arg, 0)) == ALIGNOF_EXPR)
+ ;
+ else if (!TREE_CONSTANT (arg))
+ {
+ error_at (loc, "the align argument to %<__builtin_coro_promise%>"
+ " must be a constant");
+ return error_mark_node;
+ }
+ /* Argument 2 is the direction - to / from handle address to promise
+ address. */
+ arg = CALL_EXPR_ARG (call, 2);
+ loc = EXPR_LOCATION (arg);
+ if (!TREE_CONSTANT (arg))
+ {
+ error_at (loc, "the direction argument to"
+ " %<__builtin_coro_promise%> must be a constant");
+ return error_mark_node;
+ }
+ return call;
+ break;
+ }
+ }
+}
+
+/* ================= Morph and Expand. =================
+
+ The entry point here is morph_fn_to_coro () which is called from
+ finish_function () when we have completed any template expansion.
+
+ This is preceded by helper functions that implement the phases below.
+
+ The process proceeds in four phases.
+
+ A Initial framing.
+ The user's function body is wrapped in the initial and final suspend
+ points and we begin building the coroutine frame.
+ We build empty decls for the actor and destroyer functions at this
+ time too.
+ When exceptions are enabled, the user's function body will also be
+ wrapped in a try-catch block with the catch invoking the promise
+ class 'unhandled_exception' method.
+
+ B Analysis.
+ The user's function body is analyzed to determine the suspend points,
+ if any, and to capture local variables that might persist across such
+ suspensions. In most cases, it is not necessary to capture compiler
+ temporaries, since the tree-lowering nests the suspensions correctly.
+ However, in the case of a captured reference, there is a lifetime
+ extension to the end of the full expression - which can mean across a
+ suspend point in which case it must be promoted to a frame variable.
+
+ At the conclusion of analysis, we have a conservative frame layout and
+ maps of the local variables to their frame entry points.
+
+ C Build the ramp function.
+ Carry out the allocation for the coroutine frame (NOTE; the actual size
+ computation is deferred until late in the middle end to allow for future
+ optimizations that will be allowed to elide unused frame entries).
+ We build the return object.
+
+ D Build and expand the actor and destroyer function bodies.
+ The destroyer is a trivial shim that sets a bit to indicate that the
+ destroy dispatcher should be used and then calls into the actor.
+
+ The actor function is the implementation of the user's state machine.
+ The current suspend point is noted in an index.
+ Each suspend point is encoded as a pair of internal functions, one in
+ the relevant dispatcher, and one representing the suspend point.
+
+ During this process, the user's local variables and the proxies for the
+ self-handle and the promise class instance are re-written to their
+ coroutine frame equivalents.
+
+ The complete bodies for the ramp, actor and destroy function are passed
+ back to finish_function for folding and gimplification. */
+
+/* Helpers to build EXPR_STMT and void-cast EXPR_STMT, common ops. */
+
+static tree
+coro_build_expr_stmt (tree expr, location_t loc)
+{
+ return maybe_cleanup_point_expr_void (build_stmt (loc, EXPR_STMT, expr));
+}
+
+static tree
+coro_build_cvt_void_expr_stmt (tree expr, location_t loc)
+{
+ tree t = build1 (CONVERT_EXPR, void_type_node, expr);
+ return coro_build_expr_stmt (t, loc);
+}
+
+/* Helpers for label creation:
+ 1. Create a named label in the specified context. */
+
+static tree
+create_anon_label_with_ctx (location_t loc, tree ctx)
+{
+ tree lab = build_decl (loc, LABEL_DECL, NULL_TREE, void_type_node);
+
+ DECL_CONTEXT (lab) = ctx;
+ DECL_ARTIFICIAL (lab) = true;
+ DECL_IGNORED_P (lab) = true;
+ TREE_USED (lab) = true;
+ return lab;
+}
+
+/* 2. Create a named label in the specified context. */
+
+static tree
+create_named_label_with_ctx (location_t loc, const char *name, tree ctx)
+{
+ tree lab_id = get_identifier (name);
+ tree lab = define_label (loc, lab_id);
+ DECL_CONTEXT (lab) = ctx;
+ DECL_ARTIFICIAL (lab) = true;
+ TREE_USED (lab) = true;
+ return lab;
+}
+
+struct proxy_replace
+{
+ tree from, to;
+};
+
+static tree
+replace_proxy (tree *here, int *do_subtree, void *d)
+{
+ proxy_replace *data = (proxy_replace *) d;
+
+ if (*here == data->from)
+ {
+ *here = data->to;
+ *do_subtree = 0;
+ }
+ else
+ *do_subtree = 1;
+ return NULL_TREE;
+}
+
+/* Support for expansion of co_return statements. */
+
+struct coro_ret_data
+{
+ tree promise_proxy;
+ tree real_promise;
+ tree fs_label;
+};
+
+/* If this is a coreturn statement (or one wrapped in a cleanup) then
+ return the list of statements to replace it. */
+
+static tree
+coro_maybe_expand_co_return (tree co_ret_expr, coro_ret_data *data)
+{
+ /* Look inside <(void) (expr)> cleanup */
+ if (TREE_CODE (co_ret_expr) == CLEANUP_POINT_EXPR)
+ co_ret_expr = TREE_OPERAND (co_ret_expr, 0);
+
+ if (TREE_CODE (co_ret_expr) != CO_RETURN_EXPR)
+ return NULL_TREE;
+
+ location_t loc = EXPR_LOCATION (co_ret_expr);
+ tree expr = TREE_OPERAND (co_ret_expr, 0);
+ tree call = TREE_OPERAND (co_ret_expr, 1);
+ tree stmt_list = NULL;
+ if (expr && VOID_TYPE_P (TREE_TYPE (expr)))
+ {
+ /* [stmt.return.coroutine], 2.2
+ If expr is present and void, it is placed immediately before
+ the call for return_void; */
+ expr = maybe_cleanup_point_expr_void (expr);
+ append_to_statement_list (expr, &stmt_list);
+ }
+
+ /* Now replace the promise proxy with its real value. */
+ proxy_replace p_data;
+ p_data.from = data->promise_proxy;
+ p_data.to = data->real_promise;
+ cp_walk_tree (&call, replace_proxy, &p_data, NULL);
+
+ /* The types of p.return_void and p.return_value are not explicitly stated
+ at least in n4835, it is expected that they will return void. */
+ call = maybe_cleanup_point_expr_void (call);
+ append_to_statement_list (call, &stmt_list);
+ tree r = build1_loc (loc, GOTO_EXPR, void_type_node, data->fs_label);
+ append_to_statement_list (r, &stmt_list);
+ return stmt_list;
+}
+
+/* Callback that rewrites co_return as per [stmt.return.coroutine]
+ - for co_return;
+ { p.return_void (); goto final_suspend; }
+ - for co_return [void expr];
+ { expr; p.return_void(); goto final_suspend;}
+ - for co_return [non void expr];
+ { p.return_value(expr); goto final_suspend; } */
+
+static tree
+co_return_expander (tree *stmt, int *do_subtree, void *d)
+{
+ coro_ret_data *data = (coro_ret_data *) d;
+
+ /* To avoid nesting statement lists, walk them and insert as needed. */
+ if (TREE_CODE (*stmt) == STATEMENT_LIST)
+ {
+ tree_stmt_iterator i;
+ for (i = tsi_start (*stmt); !tsi_end_p (i); tsi_next (&i))
+ {
+ tree *new_stmt = tsi_stmt_ptr (i);
+ tree replace = coro_maybe_expand_co_return (*new_stmt, data);
+ /* If we got something, it will be list and we want to splice
+ it in. */
+ if (replace != NULL_TREE)
+ {
+ /* Splice it in ... */
+ tsi_link_before (&i, replace, TSI_SAME_STMT);
+ /* ... and delete what we expanded. */
+ tsi_delink (&i);
+ /* Maybe, even likely, we replaced the last in the list. */
+ if (tsi_end_p (i))
+ break;
+ }
+ else /* Continue the walk. */
+ cp_walk_tree (new_stmt, co_return_expander, d, NULL);
+ }
+ *do_subtree = 0; /* Done subtrees. */
+ }
+ else
+ {
+ /* We might have a single co_return statement, in which case, we do
+ have to replace it with a list. */
+ tree replace = coro_maybe_expand_co_return (*stmt, data);
+ if (replace != NULL_TREE)
+ {
+ *stmt = replace;
+ *do_subtree = 0; /* Done here. */
+ }
+ }
+ return NULL_TREE;
+}
+
+/* Walk the original function body, rewriting co_returns. */
+
+static tree
+expand_co_returns (tree *fnbody, tree promise_proxy, tree promise,
+ tree fs_label)
+{
+ coro_ret_data data = {promise_proxy, promise, fs_label};
+ cp_walk_tree (fnbody, co_return_expander, &data, NULL);
+ return *fnbody;
+}
+
+/* Support for expansion of co_await statements. */
+
+struct coro_aw_data
+{
+ tree actor_fn; /* Decl for context. */
+ tree coro_fp; /* Frame pointer var. */
+ tree resume_idx; /* This is the index var in the frame. */
+ tree self_h; /* This is a handle to the current coro (frame var). */
+ tree cleanup; /* This is where to go once we complete local destroy. */
+ tree cororet; /* This is where to go if we suspend. */
+ unsigned index; /* This is our current resume index. */
+};
+
+static tree
+co_await_find_in_subtree (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
+{
+ tree **p = (tree **) d;
+ if (TREE_CODE (*stmt) == CO_AWAIT_EXPR)
+ {
+ *p = stmt;
+ return *stmt;
+ }
+ return NULL_TREE;
+}
+
+/* When we come here:
+ the first operand is the [currently unused] handle for suspend.
+ the second operand is the var to be copy-initialized
+ the third operand is 'o' (the initializer for the second)
+ as defined in [await.expr] (3.3)
+ the fourth operand is the mode as per the comment on build_co_await ().
+
+ When we leave:
+ the IFN_CO_YIELD carries the labels of the resume and destroy
+ branch targets for this await. */
+
+static tree
+co_await_expander (tree *stmt, int * /*do_subtree*/, void *d)
+{
+ if (STATEMENT_CLASS_P (*stmt) || !EXPR_P (*stmt))
+ return NULL_TREE;
+
+ coro_aw_data *data = (coro_aw_data *) d;
+
+ enum tree_code stmt_code = TREE_CODE (*stmt);
+ tree stripped_stmt = *stmt;
+
+ /* Look inside <(void) (expr)> cleanup */
+ if (stmt_code == CLEANUP_POINT_EXPR)
+ {
+ stripped_stmt = TREE_OPERAND (*stmt, 0);
+ stmt_code = TREE_CODE (stripped_stmt);
+ if (stmt_code == EXPR_STMT
+ && (TREE_CODE (EXPR_STMT_EXPR (stripped_stmt)) == CONVERT_EXPR
+ || TREE_CODE (EXPR_STMT_EXPR (stripped_stmt)) == CAST_EXPR)
+ && VOID_TYPE_P (TREE_TYPE (EXPR_STMT_EXPR (stripped_stmt))))
+ {
+ stripped_stmt = TREE_OPERAND (EXPR_STMT_EXPR (stripped_stmt), 0);
+ stmt_code = TREE_CODE (stripped_stmt);
+ }
+ }
+
+ tree *buried_stmt = NULL;
+ tree saved_co_await = NULL_TREE;
+ enum tree_code sub_code = NOP_EXPR;
+
+ if (stmt_code == EXPR_STMT
+ && TREE_CODE (EXPR_STMT_EXPR (stripped_stmt)) == CO_AWAIT_EXPR)
+ saved_co_await
+ = EXPR_STMT_EXPR (stripped_stmt); /* hopefully, a void exp. */
+ else if (stmt_code == MODIFY_EXPR || stmt_code == INIT_EXPR)
+ {
+ sub_code = TREE_CODE (TREE_OPERAND (stripped_stmt, 1));
+ if (sub_code == CO_AWAIT_EXPR)
+ saved_co_await = TREE_OPERAND (stripped_stmt, 1); /* Get the RHS. */
+ else if (tree r
+ = cp_walk_tree (&TREE_OPERAND (stripped_stmt, 1),
+ co_await_find_in_subtree, &buried_stmt, NULL))
+ saved_co_await = r;
+ }
+ else if (stmt_code == CALL_EXPR)
+ {
+ if (tree r = cp_walk_tree (&stripped_stmt, co_await_find_in_subtree,
+ &buried_stmt, NULL))
+ saved_co_await = r;
+ }
+
+ if (!saved_co_await)
+ return NULL_TREE;
+
+ /* We want to splice in the await_resume() value in some cases. */
+ tree saved_statement = *stmt;
+
+ tree actor = data->actor_fn;
+ location_t loc = EXPR_LOCATION (*stmt);
+ tree sv_handle = TREE_OPERAND (saved_co_await, 0);
+ tree var = TREE_OPERAND (saved_co_await, 1); /* frame slot. */
+ tree expr = TREE_OPERAND (saved_co_await, 2); /* initializer. */
+ tree awaiter_calls = TREE_OPERAND (saved_co_await, 3);
+
+ tree source = TREE_OPERAND (saved_co_await, 4);
+ bool is_final = (source
+ && TREE_INT_CST_LOW (source) == (int) FINAL_SUSPEND_POINT);
+ bool needs_dtor = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (var));
+ int resume_point = data->index;
+ size_t bufsize = sizeof ("destroy.") + 10;
+ char *buf = (char *) alloca (bufsize);
+ snprintf (buf, bufsize, "destroy.%d", resume_point);
+ tree destroy_label = create_named_label_with_ctx (loc, buf, actor);
+ snprintf (buf, bufsize, "resume.%d", resume_point);
+ tree resume_label = create_named_label_with_ctx (loc, buf, actor);
+ tree empty_list = build_empty_stmt (loc);
+
+ tree dtor = NULL_TREE;
+ tree await_type = TREE_TYPE (var);
+ if (needs_dtor)
+ dtor = build_special_member_call (var, complete_dtor_identifier, NULL,
+ await_type, LOOKUP_NORMAL,
+ tf_warning_or_error);
+
+ tree stmt_list = NULL;
+ /* Initialize the var from the provided 'o' expression. */
+ tree r = build2 (INIT_EXPR, await_type, var, expr);
+ r = coro_build_cvt_void_expr_stmt (r, loc);
+ append_to_statement_list (r, &stmt_list);
+
+ /* Use the await_ready() call to test if we need to suspend. */
+ tree ready_cond = TREE_VEC_ELT (awaiter_calls, 0); /* await_ready(). */
+ ready_cond = build1_loc (loc, TRUTH_NOT_EXPR, boolean_type_node, ready_cond);
+ ready_cond
+ = build1_loc (loc, CLEANUP_POINT_EXPR, boolean_type_node, ready_cond);
+
+ tree body_list = NULL;
+ tree susp_idx = build_int_cst (short_unsigned_type_node, data->index);
+ r = build2_loc (loc, MODIFY_EXPR, short_unsigned_type_node, data->resume_idx,
+ susp_idx);
+ r = coro_build_cvt_void_expr_stmt (r, loc);
+ append_to_statement_list (r, &body_list);
+
+ tree suspend = TREE_VEC_ELT (awaiter_calls, 1); /* await_suspend(). */
+
+ if (sv_handle == NULL_TREE)
+ {
+ /* void return, we just call it and hit the yield. */
+ suspend = coro_build_cvt_void_expr_stmt (suspend, loc);
+ append_to_statement_list (suspend, &body_list);
+ }
+ else if (sv_handle == boolean_type_node)
+ {
+ /* Boolean return, continue if the call returns false. */
+ suspend = build1_loc (loc, TRUTH_NOT_EXPR, boolean_type_node, suspend);
+ suspend
+ = build1_loc (loc, CLEANUP_POINT_EXPR, boolean_type_node, suspend);
+ tree go_on = build1_loc (loc, GOTO_EXPR, void_type_node, resume_label);
+ r = build3_loc (loc, COND_EXPR, void_type_node, suspend, go_on,
+ empty_list);
+ append_to_statement_list (r, &body_list);
+ }
+ else
+ {
+ r = build2_loc (loc, INIT_EXPR, TREE_TYPE (sv_handle), sv_handle,
+ suspend);
+ append_to_statement_list (r, &body_list);
+ tree resume
+ = lookup_member (TREE_TYPE (sv_handle), coro_resume_identifier, 1, 0,
+ tf_warning_or_error);
+ resume = build_new_method_call (sv_handle, resume, NULL, NULL_TREE,
+ LOOKUP_NORMAL, NULL, tf_warning_or_error);
+ resume = coro_build_cvt_void_expr_stmt (resume, loc);
+ append_to_statement_list (resume, &body_list);
+ }
+
+ tree d_l
+ = build1 (ADDR_EXPR, build_reference_type (void_type_node), destroy_label);
+ tree r_l
+ = build1 (ADDR_EXPR, build_reference_type (void_type_node), resume_label);
+ tree susp
+ = build1 (ADDR_EXPR, build_reference_type (void_type_node), data->cororet);
+ tree final_susp = build_int_cst (integer_type_node, is_final ? 1 : 0);
+
+ susp_idx = build_int_cst (integer_type_node, data->index);
+
+ tree sw = begin_switch_stmt ();
+ tree cond = build_decl (loc, VAR_DECL, NULL_TREE, integer_type_node);
+ DECL_ARTIFICIAL (cond) = 1;
+ DECL_IGNORED_P (cond) = 1;
+ layout_decl (cond, 0);
+
+ r = build_call_expr_internal_loc (loc, IFN_CO_YIELD, integer_type_node, 5,
+ susp_idx, final_susp, r_l, d_l,
+ data->coro_fp);
+ r = build2 (INIT_EXPR, integer_type_node, cond, r);
+ finish_switch_cond (r, sw);
+ r = build_case_label (build_int_cst (integer_type_node, 0), NULL_TREE,
+ create_anon_label_with_ctx (loc, actor));
+ add_stmt (r); /* case 0: */
+ /* Implement the suspend, a scope exit without clean ups. */
+ r = build_call_expr_internal_loc (loc, IFN_CO_SUSPN, void_type_node, 1, susp);
+ r = coro_build_cvt_void_expr_stmt (r, loc);
+ add_stmt (r); // goto ret;
+ r = build_case_label (build_int_cst (integer_type_node, 1), NULL_TREE,
+ create_anon_label_with_ctx (loc, actor));
+ add_stmt (r); // case 1:
+ r = build1_loc (loc, GOTO_EXPR, void_type_node, resume_label);
+ add_stmt (r); // goto resume;
+ r = build_case_label (NULL_TREE, NULL_TREE,
+ create_anon_label_with_ctx (loc, actor));
+ add_stmt (r); // default:;
+ r = build1_loc (loc, GOTO_EXPR, void_type_node, destroy_label);
+ add_stmt (r); // goto destroy;
+
+ /* part of finish switch. */
+ SWITCH_STMT_BODY (sw) = pop_stmt_list (SWITCH_STMT_BODY (sw));
+ pop_switch ();
+ tree scope = SWITCH_STMT_SCOPE (sw);
+ SWITCH_STMT_SCOPE (sw) = NULL;
+ r = do_poplevel (scope);
+ append_to_statement_list (r, &body_list);
+
+ destroy_label = build_stmt (loc, LABEL_EXPR, destroy_label);
+ append_to_statement_list (destroy_label, &body_list);
+ if (needs_dtor)
+ append_to_statement_list (dtor, &body_list);
+ r = build1_loc (loc, GOTO_EXPR, void_type_node, data->cleanup);
+ append_to_statement_list (r, &body_list);
+
+ r = build3_loc (loc, COND_EXPR, void_type_node, ready_cond, body_list,
+ empty_list);
+
+ append_to_statement_list (r, &stmt_list);
+
+ /* Resume point. */
+ resume_label = build_stmt (loc, LABEL_EXPR, resume_label);
+ append_to_statement_list (resume_label, &stmt_list);
+
+ /* This will produce the value (if one is provided) from the co_await
+ expression. */
+ tree resume_call = TREE_VEC_ELT (awaiter_calls, 2); /* await_resume(). */
+ switch (stmt_code)
+ {
+ default: /* not likely to work .. but... */
+ append_to_statement_list (resume_call, &stmt_list);
+ break;
+ case INIT_EXPR:
+ case MODIFY_EXPR:
+ case CALL_EXPR:
+ /* Replace the use of co_await by the resume expr. */
+ if (sub_code == CO_AWAIT_EXPR)
+ {
+ /* We're updating the interior of a possibly <(void) expr>cleanup. */
+ TREE_OPERAND (stripped_stmt, 1) = resume_call;
+ append_to_statement_list (saved_statement, &stmt_list);
+ }
+ else if (buried_stmt != NULL)
+ {
+ *buried_stmt = resume_call;
+ append_to_statement_list (saved_statement, &stmt_list);
+ }
+ else
+ {
+ error_at (loc, "failed to substitute the resume method in %qE",
+ saved_statement);
+ append_to_statement_list (saved_statement, &stmt_list);
+ }
+ break;
+ }
+ if (needs_dtor)
+ append_to_statement_list (dtor, &stmt_list);
+ data->index += 2;
+ *stmt = stmt_list;
+ return NULL_TREE;
+}
+
+static tree
+expand_co_awaits (tree fn, tree *fnbody, tree coro_fp, tree resume_idx,
+ tree cleanup, tree cororet, tree self_h)
+{
+ coro_aw_data data
+ = {fn, coro_fp, resume_idx, self_h, cleanup, cororet, 2};
+ cp_walk_tree (fnbody, co_await_expander, &data, NULL);
+ return *fnbody;
+}
+
+/* Suspend point hash_map. */
+
+struct suspend_point_info
+{
+ /* coro frame field type. */
+ tree awaitable_type;
+ /* coro frame field name. */
+ tree await_field_id;
+ /* suspend method return type. */
+ tree suspend_type;
+ /* suspend handle field name, NULL_TREE if not needed. */
+ tree susp_handle_id;
+};
+
+static hash_map<tree, suspend_point_info> *suspend_points;
+
+struct await_xform_data
+{
+ tree actor_frame;
+ tree promise_proxy;
+ tree real_promise;
+ tree self_h_proxy;
+ tree real_self_h;
+};
+
+/* When we built the await expressions, we didn't know the coro frame
+ layout, therefore no idea where to find the promise or where to put
+ the awaitables. Now we know these things, fill them in. */
+
+static tree
+transform_await_expr (tree await_expr, await_xform_data *xform)
+{
+ suspend_point_info *si = suspend_points->get (await_expr);
+ location_t loc = EXPR_LOCATION (await_expr);
+ if (!si)
+ {
+ error_at (loc, "no suspend point info for %qD", await_expr);
+ return error_mark_node;
+ }
+
+ /* So, on entry, we have:
+ in : CO_AWAIT_EXPR (a, e_proxy, o, awr_call_vector, mode)
+ We no longer need a [it had diagnostic value, maybe?]
+ We need to replace the promise proxy in all elements
+ We need to replace the e_proxy in the awr_call.
+ */
+
+ tree coro_frame_type = TREE_TYPE (xform->actor_frame);
+ tree ah = NULL_TREE;
+ if (si->susp_handle_id)
+ {
+ tree ah_m
+ = lookup_member (coro_frame_type, si->susp_handle_id,
+ /*protect*/ 1, /*want_type*/ 0, tf_warning_or_error);
+ ah = build_class_member_access_expr (xform->actor_frame, ah_m, NULL_TREE,
+ true, tf_warning_or_error);
+ }
+ else if (TREE_CODE (si->suspend_type) == BOOLEAN_TYPE)
+ ah = boolean_type_node;
+
+ /* Replace Op 0 with the frame slot for the temporary handle, if it's needed.
+ If there's no frame type to be stored we flag boolean_type for that case
+ and an empty pointer for void return. */
+ TREE_OPERAND (await_expr, 0) = ah;
+
+ /* Get a reference to the initial suspend var in the frame. */
+ tree as_m
+ = lookup_member (coro_frame_type, si->await_field_id,
+ /*protect*/ 1, /*want_type*/ 0, tf_warning_or_error);
+ tree as = build_class_member_access_expr (xform->actor_frame, as_m, NULL_TREE,
+ true, tf_warning_or_error);
+
+ /* Replace references to the instance proxy with the frame entry now
+ computed. */
+ proxy_replace data = {TREE_OPERAND (await_expr, 1), as};
+ cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
+
+ /* .. and replace. */
+ TREE_OPERAND (await_expr, 1) = as;
+
+ /* Now do the self_handle. */
+ data.from = xform->self_h_proxy;
+ data.to = xform->real_self_h;
+ cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
+
+ /* Now do the promise. */
+ data.from = xform->promise_proxy;
+ data.to = xform->real_promise;
+ cp_walk_tree (&await_expr, replace_proxy, &data, NULL);
+
+ return await_expr;
+}
+
+/* A wrapper for the transform_await_expr function so that it can be a
+ callback from cp_walk_tree. */
+
+static tree
+transform_await_wrapper (tree *stmt, int *do_subtree, void *d)
+{
+ if (TREE_CODE (*stmt) != CO_AWAIT_EXPR && TREE_CODE (*stmt) != CO_YIELD_EXPR)
+ return NULL_TREE;
+
+ tree await_expr = *stmt;
+ await_xform_data *xform = (await_xform_data *) d;
+
+ *stmt = transform_await_expr (await_expr, xform);
+ if (*stmt == error_mark_node)
+ *do_subtree = 0;
+ return NULL_TREE;
+}
+
+struct param_info
+{
+ tree field_id;
+ vec<tree *> *body_uses;
+ tree frame_type;
+};
+
+struct local_var_info
+{
+ tree field_id;
+ tree field_idx;
+ tree frame_type;
+ tree captured;
+ location_t def_loc;
+};
+
+/* For figuring out what local variable usage we have. */
+struct local_vars_transform
+{
+ tree context;
+ tree actor_frame;
+ tree coro_frame_type;
+ location_t loc;
+ hash_map<tree, local_var_info> *local_var_uses;
+};
+
+static tree
+transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
+{
+ local_vars_transform *lvd = (local_vars_transform *) d;
+
+ /* For each var in this bind expr (that has a frame id, which means it was
+ accessed), build a frame reference for each and then walk the bind expr
+ statements, substituting the frame ref for the original var. */
+
+ if (TREE_CODE (*stmt) == BIND_EXPR)
+ {
+ tree lvar;
+ for (lvar = BIND_EXPR_VARS (*stmt); lvar != NULL;
+ lvar = DECL_CHAIN (lvar))
+ {
+ bool existed;
+ local_var_info &local_var
+ = lvd->local_var_uses->get_or_insert (lvar, &existed);
+ gcc_checking_assert (existed);
+
+ /* Re-write the variable's context to be in the actor func. */
+ DECL_CONTEXT (lvar) = lvd->context;
+
+ /* we need to walk some of the decl trees, which might contain
+ references to vars replaced at a higher level. */
+ cp_walk_tree (&DECL_INITIAL (lvar), transform_local_var_uses, d,
+ NULL);
+ cp_walk_tree (&DECL_SIZE (lvar), transform_local_var_uses, d, NULL);
+ cp_walk_tree (&DECL_SIZE_UNIT (lvar), transform_local_var_uses, d,
+ NULL);
+
+ /* TODO: implement selective generation of fields when vars are
+ known not-used. */
+ if (local_var.field_id == NULL_TREE)
+ continue; /* Wasn't used. */
+
+ tree fld_ref
+ = lookup_member (lvd->coro_frame_type, local_var.field_id,
+ /*protect*/ 1, /*want_type*/ 0,
+ tf_warning_or_error);
+ tree fld_idx = build3_loc (lvd->loc, COMPONENT_REF, TREE_TYPE (lvar),
+ lvd->actor_frame, fld_ref, NULL_TREE);
+ local_var.field_idx = fld_idx;
+ }
+ cp_walk_tree (&BIND_EXPR_BODY (*stmt), transform_local_var_uses, d, NULL);
+ /* Now we have processed and removed references to the original vars,
+ we can drop those from the bind. */
+ for (tree *pvar = &BIND_EXPR_VARS (*stmt); *pvar != NULL;)
+ {
+ bool existed;
+ local_var_info &local_var
+ = lvd->local_var_uses->get_or_insert (*pvar, &existed);
+ gcc_checking_assert (existed);
+
+ if (local_var.field_id == NULL_TREE)
+ pvar = &DECL_CHAIN (*pvar); /* Wasn't used. */
+
+ *pvar = DECL_CHAIN (*pvar); // discard this one, we replaced it.
+ }
+
+ *do_subtree = 0; /* We've done the body already. */
+ return NULL_TREE;
+ }
+
+ tree var_decl = *stmt;
+ /* Look inside cleanups, we don't want to wrap a statement list in a
+ cleanup. */
+ bool needs_cleanup = true;
+ if (TREE_CODE (var_decl) == CLEANUP_POINT_EXPR)
+ var_decl = TREE_OPERAND (var_decl, 0);
+ else
+ needs_cleanup = false;
+
+ /* Look inside the decl_expr for the actual var. */
+ bool decl_expr_p = TREE_CODE (var_decl) == DECL_EXPR;
+ if (decl_expr_p && TREE_CODE (DECL_EXPR_DECL (var_decl)) == VAR_DECL)
+ var_decl = DECL_EXPR_DECL (var_decl);
+ else if (TREE_CODE (var_decl) != VAR_DECL)
+ return NULL_TREE;
+
+ /* VAR_DECLs that are not recorded can belong to the proxies we've placed
+ for the promise and coroutine handle(s), to global vars or to compiler
+ temporaries. Skip past these, we will handle them later. */
+ local_var_info *local_var_i = lvd->local_var_uses->get (var_decl);
+ if (local_var_i == NULL)
+ return NULL_TREE;
+
+ /* This is our revised 'local' i.e. a frame slot. */
+ tree revised = local_var_i->field_idx;
+ gcc_checking_assert (DECL_CONTEXT (var_decl) == lvd->context);
+
+ if (decl_expr_p && DECL_INITIAL (var_decl))
+ {
+ location_t loc = DECL_SOURCE_LOCATION (var_decl);
+ tree r
+ = cp_build_modify_expr (loc, revised, INIT_EXPR,
+ DECL_INITIAL (var_decl), tf_warning_or_error);
+ if (needs_cleanup)
+ r = coro_build_cvt_void_expr_stmt (r, EXPR_LOCATION (*stmt));
+ *stmt = r;
+ }
+ else
+ *stmt = revised;
+
+ if (decl_expr_p)
+ *do_subtree = 0; /* We've accounted for the nested use. */
+ return NULL_TREE;
+}
+
+/* The actor transform. */
+
+static void
+build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
+ tree orig, hash_map<tree, param_info> *param_uses,
+ hash_map<tree, local_var_info> *local_var_uses,
+ vec<tree, va_gc> *param_dtor_list, tree initial_await,
+ tree final_await, unsigned body_count)
+{
+ verify_stmt_tree (fnbody);
+ /* Some things we inherit from the original function. */
+ tree coro_frame_ptr = build_pointer_type (coro_frame_type);
+ tree handle_type = get_coroutine_handle_type (orig);
+ tree self_h_proxy = get_coroutine_self_handle_proxy (orig);
+ tree promise_type = get_coroutine_promise_type (orig);
+ tree promise_proxy = get_coroutine_promise_proxy (orig);
+ tree act_des_fn_type
+ = build_function_type_list (void_type_node, coro_frame_ptr, NULL_TREE);
+ tree act_des_fn_ptr = build_pointer_type (act_des_fn_type);
+
+ /* One param, the coro frame pointer. */
+ tree actor_fp
+ = build_lang_decl (PARM_DECL, get_identifier ("frame_ptr"), coro_frame_ptr);
+ DECL_CONTEXT (actor_fp) = actor;
+ DECL_ARG_TYPE (actor_fp) = type_passed_as (coro_frame_ptr);
+ DECL_ARGUMENTS (actor) = actor_fp;
+
+ /* A void return. */
+ tree resdecl = build_decl (loc, RESULT_DECL, 0, void_type_node);
+ DECL_ARTIFICIAL (resdecl) = 1;
+ DECL_IGNORED_P (resdecl) = 1;
+ DECL_RESULT (actor) = resdecl;
+ DECL_COROUTINE_P (actor) = 1;
+
+ /* We have a definition here. */
+ TREE_STATIC (actor) = 1;
+
+ tree actor_outer = push_stmt_list ();
+ current_stmt_tree ()->stmts_are_full_exprs_p = 1;
+ tree stmt = begin_compound_stmt (BCS_FN_BODY);
+
+ /* ??? Can we dispense with the enclosing bind if the function body does
+ not start with a bind_expr? (i.e. there's no contained scopes). */
+ tree actor_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
+ tree top_block = make_node (BLOCK);
+ BIND_EXPR_BLOCK (actor_bind) = top_block;
+
+ /* Update the block associated with the outer scope of the orig fn. */
+ tree first = expr_first (fnbody);
+ if (first && TREE_CODE (first) == BIND_EXPR)
+ {
+ /* We will discard this, since it's connected to the original scope
+ nest. */
+ tree block = BIND_EXPR_BLOCK (first);
+ if (block) // For this to be missing is probably a bug.
+ {
+ gcc_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE);
+ gcc_assert (BLOCK_CHAIN (block) == NULL_TREE);
+ BLOCK_SUPERCONTEXT (block) = top_block;
+ BLOCK_SUBBLOCKS (top_block) = block;
+ }
+ }
+
+ add_stmt (actor_bind);
+ tree actor_body = push_stmt_list ();
+
+ /* The entry point for the actor code from the ramp. */
+ tree actor_begin_label
+ = create_named_label_with_ctx (loc, "actor.begin", actor);
+ tree actor_frame = build1_loc (loc, INDIRECT_REF, coro_frame_type, actor_fp);
+
+ /* Re-write param references in the body, no code should be generated
+ here. */
+ if (DECL_ARGUMENTS (orig) && param_uses != NULL)
+ {
+ tree arg;
+ for (arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg))
+ {
+ bool existed;
+ param_info &parm = param_uses->get_or_insert (arg, &existed);
+ if (parm.field_id == NULL_TREE)
+ continue; /* Wasn't used. */
+ tree fld_ref = lookup_member (coro_frame_type, parm.field_id,
+ /*protect*/ 1, /*want_type*/ 0,
+ tf_warning_or_error);
+ tree fld_idx = build3_loc (loc, COMPONENT_REF, TREE_TYPE (arg),
+ actor_frame, fld_ref, NULL_TREE);
+ int i;
+ tree *puse;
+ FOR_EACH_VEC_ELT (*parm.body_uses, i, puse)
+ {
+ *puse = fld_idx;
+ }
+ }
+ }
+
+ /* Re-write local vars, similarly. */
+ local_vars_transform xform_vars_data
+ = {actor, actor_frame, coro_frame_type, loc, local_var_uses};
+ cp_walk_tree (&fnbody, transform_local_var_uses, &xform_vars_data, NULL);
+
+ tree resume_idx_name = get_identifier ("__resume_at");
+ tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
+ tf_warning_or_error);
+ tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, actor_frame,
+ rat_field, NULL_TREE);
+
+ tree ret_label
+ = create_named_label_with_ctx (loc, "actor.suspend.ret", actor);
+
+ tree lsb_if = begin_if_stmt ();
+ tree chkb0 = build2 (BIT_AND_EXPR, short_unsigned_type_node, rat,
+ build_int_cst (short_unsigned_type_node, 1));
+ chkb0 = build2 (NE_EXPR, short_unsigned_type_node, chkb0,
+ build_int_cst (short_unsigned_type_node, 0));
+ finish_if_stmt_cond (chkb0, lsb_if);
+
+ tree destroy_dispatcher = begin_switch_stmt ();
+ finish_switch_cond (rat, destroy_dispatcher);
+ tree ddeflab = build_case_label (NULL_TREE, NULL_TREE,
+ create_anon_label_with_ctx (loc, actor));
+ add_stmt (ddeflab);
+ tree b = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ b = coro_build_cvt_void_expr_stmt (b, loc);
+ add_stmt (b);
+
+ short unsigned lab_num = 3;
+ for (unsigned destr_pt = 0; destr_pt < body_count + 2; destr_pt++)
+ {
+ tree l_num = build_int_cst (short_unsigned_type_node, lab_num);
+ b = build_case_label (l_num, NULL_TREE,
+ create_anon_label_with_ctx (loc, actor));
+ add_stmt (b);
+ b = build_call_expr_internal_loc (loc, IFN_CO_ACTOR, void_type_node, 1,
+ l_num);
+ b = coro_build_cvt_void_expr_stmt (b, loc);
+ add_stmt (b);
+ b = build1 (GOTO_EXPR, void_type_node, CASE_LABEL (ddeflab));
+ add_stmt (b);
+ lab_num += 2;
+ }
+
+ /* Insert the prototype dispatcher. */
+ finish_switch_stmt (destroy_dispatcher);
+
+ finish_then_clause (lsb_if);
+
+ tree dispatcher = begin_switch_stmt ();
+ finish_switch_cond (rat, dispatcher);
+ b = build_case_label (build_int_cst (short_unsigned_type_node, 0), NULL_TREE,
+ create_anon_label_with_ctx (loc, actor));
+ add_stmt (b);
+ b = build1 (GOTO_EXPR, void_type_node, actor_begin_label);
+ add_stmt (b);
+
+ tree rdeflab = build_case_label (NULL_TREE, NULL_TREE,
+ create_anon_label_with_ctx (loc, actor));
+ add_stmt (rdeflab);
+ b = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ b = coro_build_cvt_void_expr_stmt (b, loc);
+ add_stmt (b);
+
+ lab_num = 2;
+ /* The final resume should be made to hit the default (trap, UB) entry. */
+ for (unsigned resu_pt = 0; resu_pt < body_count + 1; resu_pt++)
+ {
+ tree l_num = build_int_cst (short_unsigned_type_node, lab_num);
+ b = build_case_label (l_num, NULL_TREE,
+ create_anon_label_with_ctx (loc, actor));
+ add_stmt (b);
+ b = build_call_expr_internal_loc (loc, IFN_CO_ACTOR, void_type_node, 1,
+ l_num);
+ b = coro_build_cvt_void_expr_stmt (b, loc);
+ add_stmt (b);
+ b = build1 (GOTO_EXPR, void_type_node, CASE_LABEL (rdeflab));
+ add_stmt (b);
+ lab_num += 2;
+ }
+
+ /* Insert the prototype dispatcher. */
+ finish_switch_stmt (dispatcher);
+
+ finish_if_stmt (lsb_if);
+
+ tree r = build_stmt (loc, LABEL_EXPR, actor_begin_label);
+ add_stmt (r);
+
+ /* actor's version of the promise. */
+ tree ap_m = lookup_member (coro_frame_type, get_identifier ("__p"), 1, 0,
+ tf_warning_or_error);
+ tree ap = build_class_member_access_expr (actor_frame, ap_m, NULL_TREE, false,
+ tf_warning_or_error);
+
+ /* actor's coroutine 'self handle'. */
+ tree ash_m = lookup_member (coro_frame_type, get_identifier ("__self_h"), 1,
+ 0, tf_warning_or_error);
+ tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
+ false, tf_warning_or_error);
+ /* So construct the self-handle from the frame address. */
+ tree hfa_m = lookup_member (handle_type, coro_from_address_identifier, 1,
+ 0, tf_warning_or_error);
+
+ r = build1 (CONVERT_EXPR, build_pointer_type (void_type_node), actor_fp);
+ vec<tree, va_gc> *args = make_tree_vector_single (r);
+ tree hfa = build_new_method_call (ash, hfa_m, &args, NULL_TREE, LOOKUP_NORMAL,
+ NULL, tf_warning_or_error);
+ r = build2 (INIT_EXPR, handle_type, ash, hfa);
+ r = coro_build_cvt_void_expr_stmt (r, loc);
+ add_stmt (r);
+ release_tree_vector (args);
+
+ /* Now we know the real promise, and enough about the frame layout to
+ decide where to put things. */
+
+ await_xform_data xform
+ = {actor_frame, promise_proxy, ap, self_h_proxy, ash};
+
+ /* Get a reference to the initial suspend var in the frame. */
+ transform_await_expr (initial_await, &xform);
+ r = coro_build_expr_stmt (initial_await, loc);
+ add_stmt (r);
+
+ /* Now we've built the promise etc, process fnbody for co_returns.
+ We want the call to return_void () below and it has no params so
+ we can create it once here.
+ Calls to return_value () will have to be checked and created as
+ required. */
+
+ tree return_void = NULL_TREE;
+ tree rvm
+ = lookup_promise_method (orig, coro_return_void_identifier, loc,
+ /*musthave=*/ false);
+ if (rvm && rvm != error_mark_node)
+ return_void
+ = build_new_method_call (ap, rvm, NULL, NULL_TREE, LOOKUP_NORMAL, NULL,
+ tf_warning_or_error);
+
+ /* co_return branches to the final_suspend label, so declare that now. */
+ tree fs_label = create_named_label_with_ctx (loc, "final.suspend", actor);
+
+ /* Expand co_returns in the saved function body */
+ fnbody = expand_co_returns (&fnbody, promise_proxy, ap, fs_label);
+
+ /* Transform the await expressions in the function body. Only do each
+ await tree once! */
+ hash_set<tree> pset;
+ cp_walk_tree (&fnbody, transform_await_wrapper, &xform, &pset);
+
+ /* Add in our function body with the co_returns rewritten to final form. */
+ add_stmt (fnbody);
+
+ /* [stmt.return.coroutine] (2.2 : 3) if p.return_void() is a valid
+ expression, flowing off the end of a coroutine is equivalent to
+ co_return; otherwise UB.
+ We just inject the call to p.return_void() here, and fall through to
+ the final_suspend: label (eliding the goto). If the function body has
+ a co_return, then this statement will be unreachable and DCEd. */
+ if (return_void != NULL_TREE)
+ add_stmt (return_void);
+
+ /* Final suspend starts here. */
+ r = build_stmt (loc, LABEL_EXPR, fs_label);
+ add_stmt (r);
+
+ /* Set the actor pointer to null, so that 'done' will work.
+ Resume from here is UB anyway - although a 'ready' await will
+ branch to the final resume, and fall through to the destroy. */
+ tree resume_m
+ = lookup_member (coro_frame_type, get_identifier ("__resume"),
+ /*protect*/ 1, /*want_type*/ 0, tf_warning_or_error);
+ tree res_x = build_class_member_access_expr (actor_frame, resume_m, NULL_TREE,
+ false, tf_warning_or_error);
+ r = build1 (CONVERT_EXPR, act_des_fn_ptr, integer_zero_node);
+ r = build2 (INIT_EXPR, act_des_fn_ptr, res_x, r);
+ r = coro_build_cvt_void_expr_stmt (r, loc);
+ add_stmt (r);
+
+ /* Get a reference to the final suspend var in the frame. */
+ transform_await_expr (final_await, &xform);
+ r = coro_build_expr_stmt (final_await, loc);
+ add_stmt (r);
+
+ /* now do the tail of the function. */
+ tree del_promise_label
+ = create_named_label_with_ctx (loc, "coro.delete.promise", actor);
+ r = build_stmt (loc, LABEL_EXPR, del_promise_label);
+ add_stmt (r);
+
+ /* Destructors for the things we built explicitly. */
+ r = build_special_member_call (ap, complete_dtor_identifier, NULL,
+ promise_type, LOOKUP_NORMAL,
+ tf_warning_or_error);
+ add_stmt (r);
+
+ tree del_frame_label
+ = create_named_label_with_ctx (loc, "coro.delete.frame", actor);
+ r = build_stmt (loc, LABEL_EXPR, del_frame_label);
+ add_stmt (r);
+
+ /* Here deallocate the frame (if we allocated it), which we will have at
+ present. */
+ tree fnf_m
+ = lookup_member (coro_frame_type, get_identifier ("__frame_needs_free"), 1,
+ 0, tf_warning_or_error);
+ tree fnf2_x = build_class_member_access_expr (actor_frame, fnf_m, NULL_TREE,
+ false, tf_warning_or_error);
+
+ tree need_free_if = begin_if_stmt ();
+ fnf2_x = build1 (CONVERT_EXPR, integer_type_node, fnf2_x);
+ tree cmp = build2 (NE_EXPR, integer_type_node, fnf2_x, integer_zero_node);
+ finish_if_stmt_cond (cmp, need_free_if);
+ if (param_dtor_list != NULL)
+ {
+ int i;
+ tree pid;
+ FOR_EACH_VEC_ELT (*param_dtor_list, i, pid)
+ {
+ tree m
+ = lookup_member (coro_frame_type, pid, 1, 0, tf_warning_or_error);
+ tree a = build_class_member_access_expr (actor_frame, m, NULL_TREE,
+ false, tf_warning_or_error);
+ tree t = TREE_TYPE (a);
+ tree dtor;
+ dtor
+ = build_special_member_call (a, complete_dtor_identifier, NULL, t,
+ LOOKUP_NORMAL, tf_warning_or_error);
+ add_stmt (dtor);
+ }
+ }
+
+ tree delname = ovl_op_identifier (false, DELETE_EXPR);
+ tree arg = build1 (CONVERT_EXPR, ptr_type_node, actor_fp);
+ vec<tree, va_gc> *arglist = make_tree_vector_single (arg);
+
+ /* The user can (optionally) provide a delete function in the promise
+ type, it's not a failure for it to be absent. */
+ tree fns = lookup_promise_method (orig, delname, loc, false);
+ tree del_coro_fr = NULL_TREE;
+ if (fns && fns != error_mark_node)
+ {
+ del_coro_fr = lookup_arg_dependent (delname, fns, arglist);
+ if (OVL_P (del_coro_fr))
+ del_coro_fr = OVL_FIRST (del_coro_fr);
+ else
+ del_coro_fr = BASELINK_FUNCTIONS (del_coro_fr);
+
+ gcc_checking_assert (DECL_STATIC_FUNCTION_P (del_coro_fr));
+ TREE_USED (del_coro_fr) = 1;
+ del_coro_fr = build_call_expr_loc_vec (loc, del_coro_fr, arglist);
+ }
+
+ /* If that fails, then fall back to the global delete operator. */
+ if (del_coro_fr == NULL_TREE || del_coro_fr == error_mark_node)
+ {
+ fns =lookup_name_real (delname, 0, 1, /*block_p=*/true, 0, 0);
+ del_coro_fr = lookup_arg_dependent (del_coro_fr, fns, arglist);
+ del_coro_fr = build_new_function_call (del_coro_fr, &arglist, true);
+ }
+
+ del_coro_fr = coro_build_cvt_void_expr_stmt (del_coro_fr, loc);
+ add_stmt (del_coro_fr);
+ finish_then_clause (need_free_if);
+ tree scope = IF_SCOPE (need_free_if);
+ IF_SCOPE (need_free_if) = NULL;
+ r = do_poplevel (scope);
+ add_stmt (r);
+
+ /* done. */
+ r = build_stmt (loc, RETURN_EXPR, NULL);
+ TREE_NO_WARNING (r) |= 1; /* We don't want a warning about this. */
+ r = maybe_cleanup_point_expr_void (r);
+ add_stmt (r);
+
+ /* This is the suspend return point. */
+ r = build_stmt (loc, LABEL_EXPR, ret_label);
+ add_stmt (r);
+
+ r = build_stmt (loc, RETURN_EXPR, NULL);
+ TREE_NO_WARNING (r) |= 1; /* We don't want a warning about this. */
+ r = maybe_cleanup_point_expr_void (r);
+ add_stmt (r);
+
+ /* We need the resume index to work with. */
+ tree res_idx_m
+ = lookup_member (coro_frame_type, resume_idx_name,
+ /*protect*/ 1, /*want_type*/ 0, tf_warning_or_error);
+ tree res_idx
+ = build_class_member_access_expr (actor_frame, res_idx_m, NULL_TREE, false,
+ tf_warning_or_error);
+
+ /* We've now rewritten the tree and added the initial and final
+ co_awaits. Now pass over the tree and expand the co_awaits. */
+ actor_body = expand_co_awaits (actor, &actor_body, actor_fp, res_idx,
+ del_promise_label, ret_label, ash);
+
+ actor_body = pop_stmt_list (actor_body);
+ BIND_EXPR_BODY (actor_bind) = actor_body;
+
+ finish_compound_stmt (stmt);
+ DECL_SAVED_TREE (actor) = pop_stmt_list (actor_outer);
+ verify_stmt_tree (DECL_SAVED_TREE (actor));
+}
+
+/* The prototype 'destroy' function :
+ frame->__resume_at |= 1;
+ actor (frame); */
+
+static void
+build_destroy_fn (location_t loc, tree coro_frame_type, tree destroy,
+ tree actor)
+{
+ /* One param, the coro frame pointer. */
+ tree coro_frame_ptr = build_pointer_type (coro_frame_type);
+ tree destr_fp
+ = build_lang_decl (PARM_DECL, get_identifier ("frame_ptr"), coro_frame_ptr);
+ DECL_CONTEXT (destr_fp) = destroy;
+ DECL_ARG_TYPE (destr_fp) = type_passed_as (coro_frame_ptr);
+ DECL_ARGUMENTS (destroy) = destr_fp;
+
+ /* A void return. */
+ tree resdecl = build_decl (loc, RESULT_DECL, 0, void_type_node);
+ DECL_ARTIFICIAL (resdecl) = 1;
+ DECL_IGNORED_P (resdecl) = 1;
+ DECL_RESULT (destroy) = resdecl;
+
+ /* We have a definition here. */
+ TREE_STATIC (destroy) = 1;
+ DECL_COROUTINE_P (destroy) = 1;
+
+ tree destr_outer = push_stmt_list ();
+ current_stmt_tree ()->stmts_are_full_exprs_p = 1;
+ tree dstr_stmt = begin_compound_stmt (BCS_FN_BODY);
+
+ tree destr_frame = build1 (INDIRECT_REF, coro_frame_type, destr_fp);
+
+ tree resume_idx_name = get_identifier ("__resume_at");
+ tree rat_field = lookup_member (coro_frame_type, resume_idx_name, 1, 0,
+ tf_warning_or_error);
+ tree rat = build3 (COMPONENT_REF, short_unsigned_type_node, destr_frame,
+ rat_field, NULL_TREE);
+
+ /* _resume_at |= 1 */
+ tree dstr_idx = build2 (BIT_IOR_EXPR, short_unsigned_type_node, rat,
+ build_int_cst (short_unsigned_type_node, 1));
+ tree r = build2 (MODIFY_EXPR, short_unsigned_type_node, rat, dstr_idx);
+ r = coro_build_cvt_void_expr_stmt (r, loc);
+ add_stmt (r);
+
+ /* So .. call the actor .. */
+ r = build_call_expr_loc (loc, actor, 1, destr_fp);
+ r = coro_build_cvt_void_expr_stmt (r, loc);
+ add_stmt (r);
+
+ /* done. */
+ r = build_stmt (loc, RETURN_EXPR, NULL);
+ r = maybe_cleanup_point_expr_void (r);
+ add_stmt (r);
+
+ finish_compound_stmt (dstr_stmt);
+ DECL_SAVED_TREE (destroy) = pop_stmt_list (destr_outer);
+}
+
+/* Helper that returns an identifier for an appended extension to the
+ current un-mangled function name. */
+
+static tree
+get_fn_local_identifier (tree orig, const char *append)
+{
+ /* Figure out the bits we need to generate names for the outlined things
+ For consistency, this needs to behave the same way as
+ ASM_FORMAT_PRIVATE_NAME does. */
+ tree nm = DECL_NAME (orig);
+ const char *sep, *pfx = "";
+#ifndef NO_DOT_IN_LABEL
+ sep = ".";
+#else
+#ifndef NO_DOLLAR_IN_LABEL
+ sep = "$"
+#else
+ sep = "_";
+ pfx = "__";
+#endif
+#endif
+
+ char *an;
+ if (DECL_ASSEMBLER_NAME (orig))
+ an = ACONCAT ((IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (orig)), sep, append,
+ (char *) 0));
+ else if (DECL_USE_TEMPLATE (orig) && DECL_TEMPLATE_INFO (orig)
+ && DECL_TI_ARGS (orig))
+ {
+ tree tpl_args = DECL_TI_ARGS (orig);
+ an = ACONCAT ((pfx, IDENTIFIER_POINTER (nm), (char *) 0));
+ for (int i = 0; i < TREE_VEC_LENGTH (tpl_args); ++i)
+ {
+ tree typ = DECL_NAME (TYPE_NAME (TREE_VEC_ELT (tpl_args, i)));
+ an = ACONCAT ((an, sep, IDENTIFIER_POINTER (typ), (char *) 0));
+ }
+ an = ACONCAT ((an, sep, append, (char *) 0));
+ }
+ else
+ an = ACONCAT ((pfx, IDENTIFIER_POINTER (nm), sep, append, (char *) 0));
+
+ return get_identifier (an);
+}
+
+static tree
+build_init_or_final_await (location_t loc, bool is_final)
+{
+ tree suspend_alt = is_final ? coro_final_suspend_identifier
+ : coro_initial_suspend_identifier;
+ tree setup_meth = lookup_promise_method (current_function_decl, suspend_alt,
+ loc, /*musthave=*/ true);
+ if (!setup_meth || setup_meth == error_mark_node)
+ return error_mark_node;
+
+ tree s_fn = NULL_TREE;
+ tree setup_call = build_new_method_call (
+ get_coroutine_promise_proxy (current_function_decl), setup_meth, NULL,
+ NULL_TREE, LOOKUP_NORMAL, &s_fn, tf_warning_or_error);
+
+ if (!s_fn || setup_call == error_mark_node)
+ return error_mark_node;
+
+ /* So build the co_await for this */
+ /* For initial/final suspends the call is is "a" per [expr.await] 3.2. */
+ return build_co_await (loc, setup_call, (is_final ? FINAL_SUSPEND_POINT
+ : INITIAL_SUSPEND_POINT));
+}
+
+/* Callback to record the essential data for each await point found in the
+ function. */
+
+static bool
+register_await_info (tree await_expr, tree aw_type, tree aw_nam, tree susp_type,
+ tree susp_handle_nam)
+{
+ bool seen;
+ suspend_point_info &s
+ = suspend_points->get_or_insert (await_expr, &seen);
+ if (seen)
+ {
+ error_at (EXPR_LOCATION (await_expr), "duplicate info for %qE",
+ await_expr);
+ return false;
+ }
+ s.awaitable_type = aw_type;
+ s.await_field_id = aw_nam;
+ s.suspend_type = susp_type;
+ s.susp_handle_id = susp_handle_nam;
+ return true;
+}
+
+/* Small helper for the repetitive task of adding a new field to the coro
+ frame type. */
+
+static tree
+coro_make_frame_entry (tree *field_list, const char *name, tree fld_type,
+ location_t loc)
+{
+ tree id = get_identifier (name);
+ tree decl = build_decl (loc, FIELD_DECL, id, fld_type);
+ DECL_CHAIN (decl) = *field_list;
+ *field_list = decl;
+ return id;
+}
+
+struct susp_frame_data
+{
+ tree *field_list;
+ tree handle_type;
+ hash_set<tree> captured_temps;
+ vec<tree, va_gc> *to_replace;
+ vec<tree, va_gc> *block_stack;
+ unsigned count;
+ unsigned saw_awaits;
+ bool captures_temporary;
+};
+
+/* Helper to return the type of an awaiter's await_suspend() method.
+ We start with the result of the build method call, which will be either
+ a call expression (void, bool) or a target expressions (handle). */
+
+static tree
+get_await_suspend_return_type (tree aw_expr)
+{
+ tree susp_fn = TREE_VEC_ELT (TREE_OPERAND (aw_expr, 3), 1);
+ if (TREE_CODE (susp_fn) == CALL_EXPR)
+ {
+ susp_fn = CALL_EXPR_FN (susp_fn);
+ if (TREE_CODE (susp_fn) == ADDR_EXPR)
+ susp_fn = TREE_OPERAND (susp_fn, 0);
+ return TREE_TYPE (TREE_TYPE (susp_fn));
+ }
+ else if (TREE_CODE (susp_fn) == TARGET_EXPR)
+ return TREE_TYPE (susp_fn);
+ return TREE_TYPE (susp_fn);
+}
+
+/* Walk the sub-tree looking for call expressions that both capture
+ references and have compiler-temporaries as parms. */
+
+static tree
+captures_temporary (tree *stmt, int *do_subtree, void *d)
+{
+ /* Stop recursing if we see an await expression, the subtrees
+ of that will be handled when it it processed. */
+ if (TREE_CODE (*stmt) == CO_AWAIT_EXPR || TREE_CODE (*stmt) == CO_YIELD_EXPR)
+ {
+ *do_subtree = 0;
+ return NULL_TREE;
+ }
+
+ /* We're only interested in calls. */
+ if (TREE_CODE (*stmt) != CALL_EXPR)
+ return NULL_TREE;
+
+ /* Does this call capture references?
+ Strip the ADDRESS_EXPR to get the fn decl and inspect it. */
+ tree fn = TREE_OPERAND (CALL_EXPR_FN (*stmt), 0);
+ bool is_meth = TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE;
+ tree arg = TYPE_ARG_TYPES (TREE_TYPE (fn));
+ unsigned offset = 3;
+ for (unsigned anum = 0; arg != NULL; arg = TREE_CHAIN (arg), anum++)
+ {
+ tree parm_type = TREE_VALUE (arg);
+ if (anum == 0 && is_meth && INDIRECT_TYPE_P (parm_type))
+ {
+ /* Account for 'this' when the fn is a method. Unless it
+ belongs to a CTOR or DTOR. */
+ if (DECL_CONSTRUCTOR_P (fn) || DECL_DESTRUCTOR_P (fn))
+ continue;
+ }
+ else if (!TYPE_REF_P (parm_type))
+ /* If it's not a reference, we don't care. */
+ continue;
+
+ /* Fetch the value presented to the fn. */
+ tree parm = TREE_OPERAND (*stmt, anum + offset);
+
+ while (TREE_CODE (parm) == NOP_EXPR)
+ parm = TREE_OPERAND (parm, 0);
+
+ /* We only care if we're taking the addr of a temporary. */
+ if (TREE_CODE (parm) != ADDR_EXPR)
+ continue;
+
+ parm = TREE_OPERAND (parm, 0);
+ if (TREE_CODE (parm) == VAR_DECL && !DECL_ARTIFICIAL (parm))
+ /* This isn't a temporary... */
+ continue;
+
+ if (TREE_CODE (parm) == PARM_DECL)
+ /* .. nor is this... */
+ continue;
+
+ if (TREE_CODE (parm) == TARGET_EXPR)
+ {
+ /* We're taking the address of a temporary and using it as a ref. */
+ tree tvar = TREE_OPERAND (parm, 0);
+ gcc_checking_assert (DECL_ARTIFICIAL (tvar));
+
+ susp_frame_data *data = (susp_frame_data *) d;
+ data->captures_temporary = true;
+ /* Record this one so we don't duplicate, and on the first
+ occurrence note the target expr to be replaced. */
+ if (!data->captured_temps.add (tvar))
+ vec_safe_push (data->to_replace, parm);
+ /* Now see if the initializer contains any more cases. */
+ hash_set<tree> visited;
+ tree res = cp_walk_tree (&TREE_OPERAND (parm, 1),
+ captures_temporary, d, &visited);
+ if (res)
+ return res;
+ /* Otherwise, we're done with sub-trees for this. */
+ }
+ else if (TREE_CODE (parm) == CO_AWAIT_EXPR)
+ {
+ /* CO_AWAIT expressions behave in a similar manner to target
+ expressions when the await_resume call is contained in one. */
+ tree awr = TREE_OPERAND (parm, 3); /* call vector. */
+ awr = TREE_VEC_ELT (awr, 2); /* resume call. */
+ if (TREE_CODE (awr) == TARGET_EXPR)
+ {
+ tree tvar = TREE_OPERAND (awr, 0);
+ gcc_checking_assert (DECL_ARTIFICIAL (tvar));
+
+ susp_frame_data *data = (susp_frame_data *) d;
+ data->captures_temporary = true;
+ /* Use this as a place-holder. */
+ if (!data->captured_temps.add (tvar))
+ vec_safe_push (data->to_replace, parm);
+ }
+ /* We will walk the sub-trees of this co_await separately. */
+ }
+ else
+ gcc_unreachable ();
+ }
+ /* As far as it's necessary, we've walked the subtrees of the call
+ expr. */
+ do_subtree = 0;
+ return NULL_TREE;
+}
+
+/* If this is an await, then register it and decide on what coro
+ frame storage is needed.
+ If this is a co_yield (which embeds an await), drop the yield
+ and record the await (the yield was kept for diagnostics only). */
+
+static tree
+register_awaits (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
+{
+ susp_frame_data *data = (susp_frame_data *) d;
+
+ if (TREE_CODE (*stmt) != CO_AWAIT_EXPR && TREE_CODE (*stmt) != CO_YIELD_EXPR)
+ return NULL_TREE;
+
+ /* co_yield is syntactic sugar, re-write it to co_await. */
+ tree aw_expr = *stmt;
+ location_t aw_loc = EXPR_LOCATION (aw_expr); /* location of the co_xxxx. */
+ if (TREE_CODE (aw_expr) == CO_YIELD_EXPR)
+ {
+ aw_expr = TREE_OPERAND (aw_expr, 1);
+ *stmt = aw_expr;
+ }
+
+ /* Count how many awaits full expression contains. This is not the same
+ as the counter used for the function-wide await point number. */
+ data->saw_awaits++;
+
+ /* The required field has the same type as the proxy stored in the
+ await expr. */
+ tree aw_field_type = TREE_TYPE (TREE_OPERAND (aw_expr, 1));
+
+ size_t bufsize = sizeof ("__aw_s.") + 10;
+ char *buf = (char *) alloca (bufsize);
+ snprintf (buf, bufsize, "__aw_s.%d", data->count);
+ tree aw_field_nam
+ = coro_make_frame_entry (data->field_list, buf, aw_field_type, aw_loc);
+
+ /* Find out what we have to do with the awaiter's suspend method (this
+ determines if we need somewhere to stash the suspend method's handle).
+ Cache the result of this in the suspend point info.
+ [expr.await]
+ (5.1) If the result of await-ready is false, the coroutine is considered
+ suspended. Then:
+ (5.1.1) If the type of await-suspend is std::coroutine_handle<Z>,
+ await-suspend.resume() is evaluated.
+ (5.1.2) if the type of await-suspend is bool, await-suspend is evaluated,
+ and the coroutine is resumed if the result is false.
+ (5.1.3) Otherwise, await-suspend is evaluated.
+ */
+ tree susp_typ = get_await_suspend_return_type (aw_expr);
+ tree handle_field_nam;
+ if (VOID_TYPE_P (susp_typ) || TREE_CODE (susp_typ) == BOOLEAN_TYPE)
+ handle_field_nam = NULL_TREE; /* no handle is needed. */
+ else
+ {
+ snprintf (buf, bufsize, "__aw_h.%u", data->count);
+ handle_field_nam
+ = coro_make_frame_entry (data->field_list, buf, susp_typ, aw_loc);
+ }
+ register_await_info (aw_expr, aw_field_type, aw_field_nam, susp_typ,
+ handle_field_nam);
+
+ data->count++; /* Each await suspend context is unique. */
+
+ /* We now need to know if to take special action on lifetime extension
+ of temporaries captured by reference. This can only happen if such
+ a case appears in the initializer for the awaitable. The callback
+ records captured temporaries including subtrees of initializers. */
+ hash_set<tree> visited;
+ tree res = cp_walk_tree (&TREE_OPERAND (aw_expr, 2), captures_temporary, d,
+ &visited);
+ return res;
+}
+
+/* The gimplifier correctly extends the lifetime of temporaries captured
+ by reference (per. [class.temporary] (6.9) "A temporary object bound
+ to a reference parameter in a function call persists until the completion
+ of the full-expression containing the call"). However, that is not
+ sufficient to work across a suspension - and we need to promote such
+ temporaries to be regular vars that will then get a coro frame slot.
+ We don't want to incur the effort of checking for this unless we have
+ an await expression in the current full expression. */
+
+static tree
+maybe_promote_captured_temps (tree *stmt, void *d)
+{
+ susp_frame_data *awpts = (susp_frame_data *) d;
+ hash_set<tree> visited;
+ awpts->saw_awaits = 0;
+
+ /* When register_awaits sees an await, it walks the initializer for
+ that await looking for temporaries captured by reference and notes
+ them in awpts->captured_temps. We only need to take any action
+ here if the statement contained any awaits, and any of those had
+ temporaries captured by reference in the initializers for their class.
+ */
+
+ tree res = cp_walk_tree (stmt, register_awaits, d, &visited);
+ if (!res && awpts->saw_awaits > 0 && !awpts->captured_temps.is_empty ())
+ {
+ location_t sloc = EXPR_LOCATION (*stmt);
+ tree aw_bind
+ = build3_loc (sloc, BIND_EXPR, void_type_node, NULL, NULL, NULL);
+ tree aw_statement_current;
+ if (TREE_CODE (*stmt) == CLEANUP_POINT_EXPR)
+ aw_statement_current = TREE_OPERAND (*stmt, 0);
+ else
+ aw_statement_current = *stmt;
+ /* Collected the scope vars we need move the temps to regular. */
+ tree aw_bind_body = push_stmt_list ();
+ tree varlist = NULL_TREE;
+ unsigned vnum = 0;
+ while (!awpts->to_replace->is_empty ())
+ {
+ size_t bufsize = sizeof ("__aw_.tmp.") + 20;
+ char *buf = (char *) alloca (bufsize);
+ snprintf (buf, bufsize, "__aw_%d.tmp.%d", awpts->count, vnum);
+ tree to_replace = awpts->to_replace->pop ();
+ tree orig_temp;
+ if (TREE_CODE (to_replace) == CO_AWAIT_EXPR)
+ {
+ orig_temp = TREE_OPERAND (to_replace, 3);
+ orig_temp = TREE_VEC_ELT (orig_temp, 2);
+ orig_temp = TREE_OPERAND (orig_temp, 0);
+ }
+ else
+ orig_temp = TREE_OPERAND (to_replace, 0);
+
+ tree var_type = TREE_TYPE (orig_temp);
+ gcc_assert (same_type_p (TREE_TYPE (to_replace), var_type));
+ tree newvar
+ = build_lang_decl (VAR_DECL, get_identifier (buf), var_type);
+ DECL_CONTEXT (newvar) = DECL_CONTEXT (orig_temp);
+ if (DECL_SOURCE_LOCATION (orig_temp))
+ sloc = DECL_SOURCE_LOCATION (orig_temp);
+ DECL_SOURCE_LOCATION (newvar) = sloc;
+ DECL_CHAIN (newvar) = varlist;
+ varlist = newvar;
+ tree stmt
+ = build2_loc (sloc, INIT_EXPR, var_type, newvar, to_replace);
+ stmt = coro_build_cvt_void_expr_stmt (stmt, sloc);
+ add_stmt (stmt);
+ proxy_replace pr = {to_replace, newvar};
+ /* Replace all instances of that temp in the original expr. */
+ cp_walk_tree (&aw_statement_current, replace_proxy, &pr, NULL);
+ }
+ /* What's left should be the original statement with any temporaries
+ broken out. */
+ add_stmt (aw_statement_current);
+ BIND_EXPR_BODY (aw_bind) = pop_stmt_list (aw_bind_body);
+ awpts->captured_temps.empty ();
+
+ BIND_EXPR_VARS (aw_bind) = nreverse (varlist);
+ tree b_block = make_node (BLOCK);
+ if (!awpts->block_stack->is_empty ())
+ {
+ tree s_block = awpts->block_stack->last ();
+ if (s_block)
+ {
+ BLOCK_SUPERCONTEXT (b_block) = s_block;
+ BLOCK_CHAIN (b_block) = BLOCK_SUBBLOCKS (s_block);
+ BLOCK_SUBBLOCKS (s_block) = b_block;
+ }
+ }
+ BIND_EXPR_BLOCK (aw_bind) = b_block;
+
+ *stmt = aw_bind;
+ }
+ return res;
+}
+
+static tree
+await_statement_walker (tree *stmt, int *do_subtree, void *d)
+{
+ tree res = NULL_TREE;
+ susp_frame_data *awpts = (susp_frame_data *) d;
+
+ /* We might need to insert a new bind expression, and want to link it
+ into the correct scope, so keep a note of the current block scope. */
+ if (TREE_CODE (*stmt) == BIND_EXPR)
+ {
+ tree *body = &BIND_EXPR_BODY (*stmt);
+ tree blk = BIND_EXPR_BLOCK (*stmt);
+ vec_safe_push (awpts->block_stack, blk);
+
+ if (TREE_CODE (*body) == STATEMENT_LIST)
+ {
+ tree_stmt_iterator i;
+ for (i = tsi_start (*body); !tsi_end_p (i); tsi_next (&i))
+ {
+ tree *new_stmt = tsi_stmt_ptr (i);
+ if (STATEMENT_CLASS_P (*new_stmt) || !EXPR_P (*new_stmt)
+ || TREE_CODE (*new_stmt) == BIND_EXPR)
+ res = cp_walk_tree (new_stmt, await_statement_walker, d, NULL);
+ else
+ res = maybe_promote_captured_temps (new_stmt, d);
+ if (res)
+ return res;
+ }
+ *do_subtree = 0; /* Done subtrees. */
+ }
+ else if (!STATEMENT_CLASS_P (*body) && EXPR_P (*body)
+ && TREE_CODE (*body) != BIND_EXPR)
+ {
+ res = maybe_promote_captured_temps (body, d);
+ *do_subtree = 0; /* Done subtrees. */
+ }
+ awpts->block_stack->pop ();
+ }
+ else if (!STATEMENT_CLASS_P (*stmt) && EXPR_P (*stmt)
+ && TREE_CODE (*stmt) != BIND_EXPR)
+ {
+ res = maybe_promote_captured_temps (stmt, d);
+ *do_subtree = 0; /* Done subtrees. */
+ }
+ /* If it wasn't a statement list, or a single statement, continue. */
+ return res;
+}
+
+/* For figuring out what param usage we have. */
+
+struct param_frame_data
+{
+ tree *field_list;
+ hash_map<tree, param_info> *param_uses;
+ location_t loc;
+ bool param_seen;
+};
+
+static tree
+register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
+{
+ param_frame_data *data = (param_frame_data *) d;
+
+ if (TREE_CODE (*stmt) != PARM_DECL)
+ return NULL_TREE;
+
+ bool existed;
+ param_info &parm = data->param_uses->get_or_insert (*stmt, &existed);
+ gcc_checking_assert (existed);
+
+ if (parm.field_id == NULL_TREE)
+ {
+ tree actual_type = TREE_TYPE (*stmt);
+
+ if (!COMPLETE_TYPE_P (actual_type))
+ actual_type = complete_type_or_else (actual_type, *stmt);
+
+ if (TREE_CODE (actual_type) == REFERENCE_TYPE)
+ actual_type = build_pointer_type (TREE_TYPE (actual_type));
+
+ parm.frame_type = actual_type;
+ tree pname = DECL_NAME (*stmt);
+ size_t namsize = sizeof ("__parm.") + IDENTIFIER_LENGTH (pname) + 1;
+ char *buf = (char *) alloca (namsize);
+ snprintf (buf, namsize, "__parm.%s", IDENTIFIER_POINTER (pname));
+ parm.field_id
+ = coro_make_frame_entry (data->field_list, buf, actual_type, data->loc);
+ vec_alloc (parm.body_uses, 4);
+ parm.body_uses->quick_push (stmt);
+ data->param_seen = true;
+ }
+ else
+ parm.body_uses->safe_push (stmt);
+
+ return NULL_TREE;
+}
+
+/* For figuring out what local variable usage we have. */
+
+struct local_vars_frame_data
+{
+ tree *field_list;
+ hash_map<tree, local_var_info> *local_var_uses;
+ vec<local_var_info> *captures;
+ unsigned int nest_depth, bind_indx;
+ location_t loc;
+ bool saw_capture;
+ bool local_var_seen;
+};
+
+static tree
+register_local_var_uses (tree *stmt, int *do_subtree, void *d)
+{
+ local_vars_frame_data *lvd = (local_vars_frame_data *) d;
+
+ /* As we enter a bind expression - record the vars there and then recurse.
+ As we exit drop the nest depth.
+ The bind index is a growing count of how many bind indices we've seen.
+ We build a space in the frame for each local var.
+ */
+ if (TREE_CODE (*stmt) == BIND_EXPR)
+ {
+ lvd->bind_indx++;
+ lvd->nest_depth++;
+ tree lvar;
+ for (lvar = BIND_EXPR_VARS (*stmt); lvar != NULL;
+ lvar = DECL_CHAIN (lvar))
+ {
+ bool existed;
+ local_var_info &local_var
+ = lvd->local_var_uses->get_or_insert (lvar, &existed);
+ gcc_checking_assert (!existed);
+ tree lvtype = TREE_TYPE (lvar);
+ tree lvname = DECL_NAME (lvar);
+ bool captured = is_normal_capture_proxy (lvar);
+ /* Make names depth+index unique, so that we can support nested
+ scopes with identically named locals. */
+ char *buf;
+ size_t namsize = sizeof ("__lv...") + 18;
+ const char *nm = (captured ? "cp" : "lv");
+ if (lvname != NULL_TREE)
+ {
+ namsize += IDENTIFIER_LENGTH (lvname);
+ buf = (char *) alloca (namsize);
+ snprintf (buf, namsize, "__%s.%u.%u.%s", nm, lvd->bind_indx,
+ lvd->nest_depth, IDENTIFIER_POINTER (lvname));
+ }
+ else
+ {
+ namsize += 10; // 'D' followed by an unsigned.
+ buf = (char *) alloca (namsize);
+ snprintf (buf, namsize, "__%s.%u.%u.D%u", nm, lvd->bind_indx,
+ lvd->nest_depth, DECL_UID (lvar));
+ }
+ /* TODO: Figure out if we should build a local type that has any
+ excess alignment or size from the original decl. */
+ local_var.field_id
+ = coro_make_frame_entry (lvd->field_list, buf, lvtype, lvd->loc);
+ local_var.def_loc = DECL_SOURCE_LOCATION (lvar);
+ local_var.frame_type = lvtype;
+ local_var.field_idx = NULL_TREE;
+ if (captured)
+ {
+ gcc_checking_assert (DECL_INITIAL (lvar) == NULL_TREE);
+ local_var.captured = lvar;
+ lvd->captures->safe_push (local_var);
+ lvd->saw_capture = true;
+ }
+ else
+ local_var.captured = NULL;
+ lvd->local_var_seen = true;
+ /* We don't walk any of the local var sub-trees, they won't contain
+ any bind exprs. */
+ }
+ cp_walk_tree (&BIND_EXPR_BODY (*stmt), register_local_var_uses, d, NULL);
+ *do_subtree = 0; /* We've done this. */
+ lvd->nest_depth--;
+ }
+ return NULL_TREE;
+}
+
+/* Here we:
+ a) Check that the function and promise type are valid for a
+ coroutine.
+ b) Carry out the initial morph to create the skeleton of the
+ coroutine ramp function and the rewritten body.
+
+ Assumptions.
+
+ 1. We only hit this code once all dependencies are resolved.
+ 2. The function body will be either a bind expr or a statement list
+ 3. That cfun and current_function_decl are valid for the case we're
+ expanding.
+ 4. 'input_location' will be of the final brace for the function.
+
+ We do something like this:
+ declare a dummy coro frame.
+ struct _R_frame {
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ void (*__resume)(_R_frame *);
+ void (*__destroy)(_R_frame *);
+ coro1::promise_type __p;
+ bool frame_needs_free; // free the coro frame mem if set.
+ short __resume_at; // this is where clang puts it - but it's a smaller entity.
+ coro1::suspend_never_prt __is;
+ (maybe) handle_type i_hand;
+ coro1::suspend_always_prt __fs;
+ (maybe) handle_type f_hand;
+ (maybe) parameters used in the body.
+ (maybe) local variables saved
+ (maybe) trailing space.
+ }; */
+
+bool
+morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
+{
+ gcc_checking_assert (orig && TREE_CODE (orig) == FUNCTION_DECL);
+
+ if (!coro_function_valid_p (orig))
+ return false;
+
+ /* We can't validly get here with an empty statement list, since there's no
+ way for the FE to decide it's a coroutine in the absence of any code. */
+ tree fnbody = pop_stmt_list (DECL_SAVED_TREE (orig));
+ if (fnbody == NULL_TREE)
+ return false;
+
+ /* We don't have the locus of the opening brace - it's filled in later (and
+ there doesn't really seem to be any easy way to get at it).
+ The closing brace is assumed to be input_location. */
+ location_t fn_start = DECL_SOURCE_LOCATION (orig);
+ gcc_rich_location fn_start_loc (fn_start);
+
+ /* Initial processing of the captured body.
+ If we have no expressions or just an error then punt. */
+ tree body_start = expr_first (fnbody);
+ if (body_start == NULL_TREE || body_start == error_mark_node)
+ {
+ DECL_SAVED_TREE (orig) = push_stmt_list ();
+ append_to_statement_list (DECL_SAVED_TREE (orig), &fnbody);
+ return false;
+ }
+
+ /* So, we've tied off the original body. Now start the replacement.
+ If we encounter a fatal error we might return a now-empty body.
+ TODO: determine if it would help to restore the original.
+ determine if looking for more errors in coro_function_valid_p()
+ and stashing types is a better solution.
+ */
+
+ tree newbody = push_stmt_list ();
+ DECL_SAVED_TREE (orig) = newbody;
+
+ /* If our original body is noexcept, then that's what we apply to our
+ generated functions. Remember that we're NOEXCEPT and fish out the
+ contained list (we tied off to the top level already). */
+ bool is_noexcept = TREE_CODE (body_start) == MUST_NOT_THROW_EXPR;
+ if (is_noexcept)
+ {
+ /* Simplified abstract from begin_eh_spec_block, since we already
+ know the outcome. */
+ fnbody = TREE_OPERAND (body_start, 0); /* Stash the original... */
+ add_stmt (body_start); /* ... and start the new. */
+ TREE_OPERAND (body_start, 0) = push_stmt_list ();
+ }
+
+ /* Create the coro frame type, as far as it can be known at this stage.
+ 1. Types we already know. */
+
+ tree fn_return_type = TREE_TYPE (TREE_TYPE (orig));
+ gcc_assert (!VOID_TYPE_P (fn_return_type));
+ tree handle_type = get_coroutine_handle_type (orig);
+ tree promise_type = get_coroutine_promise_type (orig);
+
+ /* 2. Types we need to define or look up. */
+
+ /* We need to know, and inspect, each suspend point in the function
+ in several places. It's convenient to place this map out of line
+ since it's used from tree walk callbacks. */
+ suspend_points = new hash_map<tree, suspend_point_info>;
+
+ /* Initial and final suspend types are special in that the co_awaits for
+ them are synthetic. We need to find the type for each awaiter from
+ the coroutine promise. */
+ tree initial_await = build_init_or_final_await (fn_start, false);
+ if (initial_await == error_mark_node)
+ return false;
+ /* The type of the frame var for this is the type of its temp proxy. */
+ tree initial_suspend_type = TREE_TYPE (TREE_OPERAND (initial_await, 1));
+
+ tree final_await = build_init_or_final_await (fn_start, true);
+ if (final_await == error_mark_node)
+ return false;
+
+ /* The type of the frame var for this is the type of its temp proxy. */
+ tree final_suspend_type = TREE_TYPE (TREE_OPERAND (final_await, 1));
+
+ tree fr_name = get_fn_local_identifier (orig, "frame");
+ tree coro_frame_type = xref_tag (record_type, fr_name, ts_current, false);
+ DECL_CONTEXT (TYPE_NAME (coro_frame_type)) = current_scope ();
+ tree coro_frame_ptr = build_pointer_type (coro_frame_type);
+ tree act_des_fn_type
+ = build_function_type_list (void_type_node, coro_frame_ptr, NULL_TREE);
+ tree act_des_fn_ptr = build_pointer_type (act_des_fn_type);
+
+ /* Declare the actor function. */
+ tree actor_name = get_fn_local_identifier (orig, "actor");
+ tree actor = build_lang_decl (FUNCTION_DECL, actor_name, act_des_fn_type);
+ DECL_CONTEXT (actor) = DECL_CONTEXT (orig);
+ DECL_INITIAL (actor) = error_mark_node;
+
+ /* Declare the destroyer function. */
+ tree destr_name = get_fn_local_identifier (orig, "destroy");
+ tree destroy = build_lang_decl (FUNCTION_DECL, destr_name, act_des_fn_type);
+ DECL_CONTEXT (destroy) = DECL_CONTEXT (orig);
+ DECL_INITIAL (destroy) = error_mark_node;
+
+ /* Build our dummy coro frame layout. */
+ coro_frame_type = begin_class_definition (coro_frame_type);
+
+ tree field_list = NULL_TREE;
+ tree resume_name
+ = coro_make_frame_entry (&field_list, "__resume", act_des_fn_ptr, fn_start);
+ tree destroy_name = coro_make_frame_entry (&field_list, "__destroy",
+ act_des_fn_ptr, fn_start);
+ tree promise_name
+ = coro_make_frame_entry (&field_list, "__p", promise_type, fn_start);
+ tree fnf_name = coro_make_frame_entry (&field_list, "__frame_needs_free",
+ boolean_type_node, fn_start);
+ tree resume_idx_name
+ = coro_make_frame_entry (&field_list, "__resume_at",
+ short_unsigned_type_node, fn_start);
+
+ /* We need a handle to this coroutine, which is passed to every
+ await_suspend(). There's no point in creating it over and over. */
+ (void) coro_make_frame_entry (&field_list, "__self_h", handle_type, fn_start);
+
+ /* Initial suspend is mandated. */
+ tree init_susp_name = coro_make_frame_entry (&field_list, "__aw_s.is",
+ initial_suspend_type, fn_start);
+
+ /* Figure out if we need a saved handle from the awaiter type. */
+ tree ret_typ = get_await_suspend_return_type (initial_await);
+ tree init_hand_name;
+ if (VOID_TYPE_P (ret_typ) || TREE_CODE (ret_typ) == BOOLEAN_TYPE)
+ init_hand_name = NULL_TREE; /* no handle is needed. */
+ else
+ {
+ init_hand_name
+ = coro_make_frame_entry (&field_list, "__ih", ret_typ, fn_start);
+ }
+
+ register_await_info (initial_await, initial_suspend_type, init_susp_name,
+ ret_typ, init_hand_name);
+
+ /* Now insert the data for any body await points, at this time we also need
+ to promote any temporaries that are captured by reference (to regular
+ vars) they will get added to the coro frame along with other locals. */
+ susp_frame_data body_aw_points
+ = {&field_list, handle_type, hash_set<tree> (), NULL, NULL, 0, 0, false};
+ body_aw_points.to_replace = make_tree_vector ();
+ body_aw_points.block_stack = make_tree_vector ();
+ cp_walk_tree (&fnbody, await_statement_walker, &body_aw_points, NULL);
+
+ /* Final suspend is mandated. */
+ tree fin_susp_name = coro_make_frame_entry (&field_list, "__aw_s.fs",
+ final_suspend_type, fn_start);
+
+ ret_typ = get_await_suspend_return_type (final_await);
+ tree fin_hand_name;
+ if (VOID_TYPE_P (ret_typ) || TREE_CODE (ret_typ) == BOOLEAN_TYPE)
+ fin_hand_name = NULL_TREE; /* no handle is needed. */
+ else
+ {
+ fin_hand_name
+ = coro_make_frame_entry (&field_list, "__fh", ret_typ, fn_start);
+ }
+
+ register_await_info (final_await, final_suspend_type, fin_susp_name,
+ void_type_node, fin_hand_name);
+
+ /* 3. Now add in fields for function params (if there are any) that are used
+ within the function body. This is conservative; we can't tell at this
+ stage if such uses might be optimized away, or if they might turn out not
+ to persist across any suspend points. Of course, even if they don't
+ persist across suspend points, when the actor is out of line the saved
+ frame version is still needed. */
+ hash_map<tree, param_info> *param_uses = NULL;
+ if (DECL_ARGUMENTS (orig))
+ {
+ /* Build a hash map with an entry for each param.
+ The key is the param tree.
+ Then we have an entry for the frame field name.
+ Then a cache for the field ref when we come to use it.
+ Then a tree list of the uses.
+ The second two entries start out empty - and only get populated
+ when we see uses. */
+ param_uses = new hash_map<tree, param_info>;
+
+ for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
+ arg = DECL_CHAIN (arg))
+ {
+ bool existed;
+ param_info &parm = param_uses->get_or_insert (arg, &existed);
+ gcc_checking_assert (!existed);
+ parm.field_id = NULL_TREE;
+ parm.body_uses = NULL;
+ }
+
+ param_frame_data param_data
+ = {&field_list, param_uses, fn_start, false};
+ /* We want to record every instance of param's use, so don't include
+ a 'visited' hash_set. */
+ cp_walk_tree (&fnbody, register_param_uses, ¶m_data, NULL);
+
+ /* If no uses for any param were seen, act as if there were no
+ params (it could be that they are only used to construct the
+ promise). */
+ if (!param_data.param_seen)
+ {
+ delete param_uses;
+ param_uses = NULL;
+ }
+ }
+
+ /* 4. Now make space for local vars, this is conservative again, and we
+ would expect to delete unused entries later. */
+ hash_map<tree, local_var_info> local_var_uses;
+ auto_vec<local_var_info> captures;
+
+ local_vars_frame_data local_vars_data
+ = {&field_list, &local_var_uses, &captures, 0, 0, fn_start, false, false};
+ cp_walk_tree (&fnbody, register_local_var_uses, &local_vars_data, NULL);
+
+ /* Tie off the struct for now, so that we can build offsets to the
+ known entries. */
+ TYPE_FIELDS (coro_frame_type) = field_list;
+ TYPE_BINFO (coro_frame_type) = make_tree_binfo (0);
+ BINFO_OFFSET (TYPE_BINFO (coro_frame_type)) = size_zero_node;
+ BINFO_TYPE (TYPE_BINFO (coro_frame_type)) = coro_frame_type;
+
+ coro_frame_type = finish_struct (coro_frame_type, NULL_TREE);
+
+ /* Ramp: */
+ /* Now build the ramp function pieces. */
+ tree ramp_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
+ add_stmt (ramp_bind);
+ tree ramp_body = push_stmt_list ();
+ tree empty_list = build_empty_stmt (fn_start);
+
+ tree coro_fp = build_lang_decl (VAR_DECL, get_identifier ("coro.frameptr"),
+ coro_frame_ptr);
+ tree varlist = coro_fp;
+ local_var_info *cap;
+ if (!captures.is_empty())
+ for (int ix = 0; captures.iterate (ix, &cap); ix++)
+ {
+ if (cap->field_id == NULL_TREE)
+ continue;
+ tree t = cap->captured;
+ DECL_CHAIN (t) = varlist;
+ varlist = t;
+ }
+
+ /* Collected the scope vars we need ... only one for now. */
+ BIND_EXPR_VARS (ramp_bind) = nreverse (varlist);
+
+ /* We're now going to create a new top level scope block for the ramp
+ function. */
+ tree top_block = make_node (BLOCK);
+
+ BIND_EXPR_BLOCK (ramp_bind) = top_block;
+ BLOCK_VARS (top_block) = BIND_EXPR_VARS (ramp_bind);
+ BLOCK_SUBBLOCKS (top_block) = NULL_TREE;
+
+ /* The decl_expr for the coro frame pointer, initialize to zero so that we
+ can pass it to the IFN_CO_FRAME (since there's no way to pass a type,
+ directly apparently). This avoids a "used uninitialized" warning. */
+ tree r = build_stmt (fn_start, DECL_EXPR, coro_fp);
+ tree zeroinit = build1 (CONVERT_EXPR, coro_frame_ptr, integer_zero_node);
+ r = build2 (INIT_EXPR, TREE_TYPE (coro_fp), coro_fp, zeroinit);
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+
+ /* We are going to copy the behavior of clang w.r.t to failed allocation
+ of the coroutine frame.
+ 1. If the promise has a 'get_return_object_on_allocation_failure()'
+ method, then we use a nothrow new and check the return value, calling
+ the method on failure to initialize an early return.
+ 2. Otherwise, we call new and the ramp is expected to terminate with an
+ unhandled exception in the case of failure to allocate.
+
+ The get_return_object_on_allocation_failure() must be a static method.
+ */
+ tree grooaf_meth
+ = lookup_promise_method (orig, coro_gro_on_allocation_fail_identifier,
+ fn_start, /*musthave=*/ false);
+
+ /* The CO_FRAME internal function is a mechanism to allow the middle end
+ to adjust the allocation in response to optimisations. We provide the
+ current conservative estimate of the frame size (as per the current)
+ computed layout. */
+ tree resizeable
+ = build_call_expr_internal_loc (fn_start, IFN_CO_FRAME, size_type_node, 2,
+ TYPE_SIZE_UNIT (coro_frame_type), coro_fp);
+
+ /* We need to adjust the operator new call as per the description above when
+ there is a return on allocation fail function provided in the promise. */
+ tree grooaf = NULL_TREE;
+ vec<tree, va_gc> *arglist;
+ vec_alloc (arglist, 2);
+ arglist->quick_push (resizeable);
+ if (grooaf_meth && BASELINK_P (grooaf_meth))
+ {
+ tree fn = BASELINK_FUNCTIONS (grooaf_meth);
+ if (TREE_CODE (fn) == FUNCTION_DECL && DECL_STATIC_FUNCTION_P (fn))
+ {
+ grooaf = build_call_expr_loc (fn_start, fn, 0);
+ TREE_USED (fn) = 1;
+ }
+ tree nth_ns = lookup_qualified_name (std_node, get_identifier ("nothrow"),
+ 0, /*complain=*/ true, false);
+ arglist->quick_push (nth_ns);
+ }
+
+ /* Allocate the frame. */
+
+ tree nwname = ovl_op_identifier (false, NEW_EXPR);
+ /* The user can (optionally) provide an allocation function in the promise
+ type, it's not a failure for it to be absent. */
+ tree fns = lookup_promise_method (orig, nwname, fn_start,
+ /*musthave=*/ false);
+ tree new_fn = NULL_TREE;
+ if (fns && fns != error_mark_node)
+ {
+ new_fn = lookup_arg_dependent (nwname, fns, arglist);
+ if (OVL_P (new_fn))
+ new_fn = OVL_FIRST (new_fn);
+ else
+ new_fn = BASELINK_FUNCTIONS (new_fn);
+
+ gcc_checking_assert (DECL_STATIC_FUNCTION_P (new_fn));
+ TREE_USED (new_fn) = 1;
+ new_fn = build_call_expr_loc_vec (fn_start, new_fn, arglist);
+ }
+
+ /* If that fails, then fall back to the global operator new. */
+ if (new_fn == NULL_TREE || new_fn == error_mark_node)
+ {
+ fns =lookup_name_real (nwname, 0, 1, /*block_p=*/true, 0, 0);
+ new_fn = lookup_arg_dependent (nwname, fns, arglist);
+ new_fn = build_new_function_call (new_fn, &arglist, /*complain=*/ true);
+ }
+
+ tree allocated = build1 (CONVERT_EXPR, coro_frame_ptr, new_fn);
+ r = build2 (INIT_EXPR, TREE_TYPE (coro_fp), coro_fp, allocated);
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+
+ /* If the user provided a method to return an object on alloc fail, then
+ check the returned pointer and call the func if it's null.
+ Otherwise, no check, and we fail for noexcept/fno-exceptions cases. */
+
+ if (grooaf)
+ {
+ tree cfra_label
+ = create_named_label_with_ctx (fn_start, "coro.frame.active",
+ current_scope ());
+ tree early_ret_list = NULL;
+ /* init the retval using the user's func. */
+ r = build2 (INIT_EXPR, TREE_TYPE (DECL_RESULT (orig)), DECL_RESULT (orig),
+ grooaf);
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ append_to_statement_list (r, &early_ret_list);
+ // We know it's the correct type.
+ r = DECL_RESULT (orig);
+ r = build_stmt (fn_start, RETURN_EXPR, r);
+ TREE_NO_WARNING (r) |= 1;
+ r = maybe_cleanup_point_expr_void (r);
+ append_to_statement_list (r, &early_ret_list);
+
+ tree goto_st = NULL;
+ r = build1 (GOTO_EXPR, void_type_node, cfra_label);
+ append_to_statement_list (r, &goto_st);
+
+ tree ckk = build1 (CONVERT_EXPR, coro_frame_ptr, integer_zero_node);
+ tree ckz = build2 (EQ_EXPR, boolean_type_node, coro_fp, ckk);
+ r = build3 (COND_EXPR, void_type_node, ckz, early_ret_list, empty_list);
+ add_stmt (r);
+
+ cfra_label = build_stmt (fn_start, LABEL_EXPR, cfra_label);
+ add_stmt (cfra_label);
+ }
+
+ /* deref the frame pointer, to use in member access code. */
+ tree deref_fp = build_x_arrow (fn_start, coro_fp, tf_warning_or_error);
+
+ /* For now, we always assume that this needs destruction, there's no impl.
+ for frame allocation elision. */
+ tree fnf_m
+ = lookup_member (coro_frame_type, fnf_name, 1, 0, tf_warning_or_error);
+ tree fnf_x = build_class_member_access_expr (deref_fp, fnf_m, NULL_TREE,
+ false, tf_warning_or_error);
+ r = build2 (INIT_EXPR, boolean_type_node, fnf_x, boolean_true_node);
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+
+ /* Put the resumer and destroyer functions in. */
+
+ tree actor_addr = build1 (ADDR_EXPR, act_des_fn_ptr, actor);
+ tree resume_m
+ = lookup_member (coro_frame_type, resume_name,
+ /*protect*/ 1, /*want_type*/ 0, tf_warning_or_error);
+ tree resume_x = build_class_member_access_expr (deref_fp, resume_m, NULL_TREE,
+ false, tf_warning_or_error);
+ r = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, resume_x, actor_addr);
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+
+ tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr, destroy);
+ tree destroy_m
+ = lookup_member (coro_frame_type, destroy_name,
+ /*protect*/ 1, /*want_type*/ 0, tf_warning_or_error);
+ tree destroy_x
+ = build_class_member_access_expr (deref_fp, destroy_m, NULL_TREE, false,
+ tf_warning_or_error);
+ r = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, destroy_x, destroy_addr);
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+
+ /* Set up the promise. */
+ tree promise_m
+ = lookup_member (coro_frame_type, promise_name,
+ /*protect*/ 1, /*want_type*/ 0, tf_warning_or_error);
+
+ tree p = build_class_member_access_expr (deref_fp, promise_m, NULL_TREE,
+ false, tf_warning_or_error);
+
+ if (TYPE_NEEDS_CONSTRUCTING (promise_type))
+ {
+ /* Do a placement new constructor for the promise type (we never call
+ the new operator, just the constructor on the object in place in the
+ frame).
+
+ First try to find a constructor with the same parameter list as the
+ original function (if it has params), failing that find a constructor
+ with no parameter list.
+ */
+
+ if (DECL_ARGUMENTS (orig))
+ {
+ vec<tree, va_gc> *args = make_tree_vector ();
+ tree arg;
+ for (arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg))
+ vec_safe_push (args, arg);
+ r = build_special_member_call (p, complete_ctor_identifier, &args,
+ promise_type, LOOKUP_NORMAL, tf_none);
+ release_tree_vector (args);
+ }
+ else
+ r = NULL_TREE;
+
+ if (r == NULL_TREE || r == error_mark_node)
+ r = build_special_member_call (p, complete_ctor_identifier, NULL,
+ promise_type, LOOKUP_NORMAL,
+ tf_warning_or_error);
+
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+ }
+
+ /* Copy in any of the function params we found to be used.
+ Param types with non-trivial dtors will have to be moved into position
+ and the dtor run before the frame is freed. */
+ vec<tree, va_gc> *param_dtor_list = NULL;
+ if (DECL_ARGUMENTS (orig) && param_uses != NULL)
+ {
+ tree arg;
+ for (arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg))
+ {
+ bool existed;
+ param_info &parm = param_uses->get_or_insert (arg, &existed);
+ if (parm.field_id == NULL_TREE)
+ continue; /* Wasn't used. */
+
+ tree fld_ref = lookup_member (coro_frame_type, parm.field_id,
+ /*protect*/ 1, /*want_type*/ 0,
+ tf_warning_or_error);
+ tree fld_idx
+ = build_class_member_access_expr (deref_fp, fld_ref, NULL_TREE,
+ false, tf_warning_or_error);
+
+ if (TYPE_NEEDS_CONSTRUCTING (parm.frame_type))
+ {
+ vec<tree, va_gc> *p_in;
+ if (TYPE_REF_P (DECL_ARG_TYPE (arg))
+ && (CLASSTYPE_LAZY_MOVE_CTOR (parm.frame_type)
+ || CLASSTYPE_LAZY_MOVE_ASSIGN (parm.frame_type)
+ || classtype_has_move_assign_or_move_ctor_p
+ (parm.frame_type, /* user-declared */ true)))
+ p_in = make_tree_vector_single (rvalue (arg));
+ else
+ p_in = make_tree_vector_single (arg);
+ /* Construct in place or move as relevant. */
+ r = build_special_member_call (fld_idx, complete_ctor_identifier,
+ &p_in, parm.frame_type,
+ LOOKUP_NORMAL,
+ tf_warning_or_error);
+ release_tree_vector (p_in);
+ if (param_dtor_list == NULL)
+ param_dtor_list = make_tree_vector ();
+ vec_safe_push (param_dtor_list, parm.field_id);
+ }
+ else
+ {
+ if (!same_type_p (parm.frame_type, DECL_ARG_TYPE (arg)))
+ r = build1_loc (DECL_SOURCE_LOCATION (arg), CONVERT_EXPR,
+ parm.frame_type, arg);
+ else
+ r = arg;
+ r = build_modify_expr (fn_start, fld_idx, parm.frame_type,
+ INIT_EXPR, DECL_SOURCE_LOCATION (arg), r,
+ TREE_TYPE (r));
+ }
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+ }
+ }
+
+ vec<tree, va_gc> *captures_dtor_list = NULL;
+ while (!captures.is_empty())
+ {
+ local_var_info cap = captures.pop();
+ if (cap.field_id == NULL_TREE)
+ continue;
+
+ tree fld_ref = lookup_member (coro_frame_type, cap.field_id,
+ /*protect*/ 1, /*want_type*/ 0,
+ tf_warning_or_error);
+ tree fld_idx
+ = build_class_member_access_expr (deref_fp, fld_ref, NULL_TREE,
+ false, tf_warning_or_error);
+
+ tree cap_type = cap.frame_type;
+
+ /* When we have a reference, we do not want to change the referenced
+ item, but actually to set the reference to the proxy var. */
+ if (REFERENCE_REF_P (fld_idx))
+ fld_idx = TREE_OPERAND (fld_idx, 0);
+
+ if (TYPE_NEEDS_CONSTRUCTING (cap_type))
+ {
+ vec<tree, va_gc> *p_in;
+ if (TYPE_REF_P (cap_type)
+ && (CLASSTYPE_LAZY_MOVE_CTOR (cap_type)
+ || CLASSTYPE_LAZY_MOVE_ASSIGN (cap_type)
+ || classtype_has_move_assign_or_move_ctor_p
+ (cap_type, /* user-declared */ true)))
+ p_in = make_tree_vector_single (rvalue (cap.captured));
+ else
+ p_in = make_tree_vector_single (cap.captured);
+ /* Construct in place or move as relevant. */
+ r = build_special_member_call (fld_idx, complete_ctor_identifier,
+ &p_in, cap_type, LOOKUP_NORMAL,
+ tf_warning_or_error);
+ release_tree_vector (p_in);
+ if (captures_dtor_list == NULL)
+ captures_dtor_list = make_tree_vector ();
+ vec_safe_push (captures_dtor_list, cap.field_id);
+ }
+ else
+ {
+ if (!same_type_p (cap_type, TREE_TYPE (cap.captured)))
+ r = build1_loc (DECL_SOURCE_LOCATION (cap.captured), CONVERT_EXPR,
+ cap_type, cap.captured);
+ else
+ r = cap.captured;
+ r = build_modify_expr (fn_start, fld_idx, cap_type,
+ INIT_EXPR, DECL_SOURCE_LOCATION (cap.captured),
+ r, TREE_TYPE (r));
+ }
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+ }
+
+ /* Set up a new bind context for the GRO. */
+ tree gro_context_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
+ /* Make and connect the scope blocks. */
+ tree gro_block = make_node (BLOCK);
+ BLOCK_SUPERCONTEXT (gro_block) = top_block;
+ BLOCK_SUBBLOCKS (top_block) = gro_block;
+ BIND_EXPR_BLOCK (gro_context_bind) = gro_block;
+ add_stmt (gro_context_bind);
+
+ tree gro_meth = lookup_promise_method (orig,
+ coro_get_return_object_identifier,
+ fn_start, /*musthave=*/ true );
+ tree get_ro
+ = build_new_method_call (p, gro_meth, NULL, NULL_TREE, LOOKUP_NORMAL, NULL,
+ tf_warning_or_error);
+ /* Without a return object we haven't got much clue what's going on. */
+ if (get_ro == error_mark_node)
+ {
+ BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);
+ DECL_SAVED_TREE (orig) = newbody;
+ return false;
+ }
+
+ tree gro_context_body = push_stmt_list ();
+ tree gro, gro_bind_vars;
+ if (same_type_p (TREE_TYPE (get_ro), fn_return_type))
+ {
+ gro = DECL_RESULT (orig);
+ gro_bind_vars = NULL_TREE; // We don't need a separate var.
+ }
+ else
+ {
+ gro = build_lang_decl (VAR_DECL, get_identifier ("coro.gro"),
+ TREE_TYPE (TREE_OPERAND (get_ro, 0)));
+ DECL_CONTEXT (gro) = current_scope ();
+ r = build_stmt (fn_start, DECL_EXPR, gro);
+ add_stmt (r);
+ gro_bind_vars = gro; // We need a temporary var.
+ }
+
+ // init our actual var.
+ r = build2_loc (fn_start, INIT_EXPR, TREE_TYPE (gro), gro, get_ro);
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+
+ /* Initialize the resume_idx_name to 0, meaning "not started". */
+ tree resume_idx_m
+ = lookup_member (coro_frame_type, resume_idx_name,
+ /*protect*/ 1, /*want_type*/ 0, tf_warning_or_error);
+ tree resume_idx
+ = build_class_member_access_expr (deref_fp, resume_idx_m, NULL_TREE, false,
+ tf_warning_or_error);
+ r = build_int_cst (short_unsigned_type_node, 0);
+ r = build2_loc (fn_start, INIT_EXPR, short_unsigned_type_node, resume_idx, r);
+ r = coro_build_cvt_void_expr_stmt (r, fn_start);
+ add_stmt (r);
+
+ /* So .. call the actor .. */
+ r = build_call_expr_loc (fn_start, actor, 1, coro_fp);
+ r = maybe_cleanup_point_expr_void (r);
+ add_stmt (r);
+
+ /* Switch to using 'input_location' as the loc, since we're now more
+ logically doing things related to the end of the function. */
+ /* done, we just need the return value. */
+ bool no_warning;
+ if (same_type_p (TREE_TYPE (gro), fn_return_type))
+ {
+ /* Already got the result. */
+ r = check_return_expr (DECL_RESULT (orig), &no_warning);
+ }
+ else
+ {
+ // construct the return value with a single GRO param.
+ vec<tree, va_gc> *args = make_tree_vector_single (gro);
+ r = build_special_member_call (DECL_RESULT (orig),
+ complete_ctor_identifier, &args,
+ fn_return_type, LOOKUP_NORMAL,
+ tf_warning_or_error);
+ r = coro_build_cvt_void_expr_stmt (r, input_location);
+ add_stmt (r);
+ release_tree_vector (args);
+ }
+
+ r = build_stmt (input_location, RETURN_EXPR, DECL_RESULT (orig));
+ TREE_NO_WARNING (r) |= no_warning;
+ r = maybe_cleanup_point_expr_void (r);
+ add_stmt (r);
+ BIND_EXPR_VARS (gro_context_bind) = gro_bind_vars;
+ BIND_EXPR_BODY (gro_context_bind) = pop_stmt_list (gro_context_body);
+ BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);
+
+ /* We know the "real" promise and have a frame layout with a slot for each
+ suspend point, so we can build an actor function (which contains the
+ functionality for both 'resume' and 'destroy').
+
+ wrap the function body in a try {} catch (...) {} block, if exceptions
+ are enabled. */
+
+ /* First make a new block for the body - that will be embedded in the
+ re-written function. */
+ tree first = expr_first (fnbody);
+ bool orig_fn_has_outer_bind = false;
+ tree replace_blk = NULL_TREE;
+ if (first && TREE_CODE (first) == BIND_EXPR)
+ {
+ orig_fn_has_outer_bind = true;
+ tree block = BIND_EXPR_BLOCK (first);
+ replace_blk = make_node (BLOCK);
+ if (block) // missing block is probably an error.
+ {
+ gcc_assert (BLOCK_SUPERCONTEXT (block) == NULL_TREE);
+ gcc_assert (BLOCK_CHAIN (block) == NULL_TREE);
+ BLOCK_VARS (replace_blk) = BLOCK_VARS (block);
+ BLOCK_SUBBLOCKS (replace_blk) = BLOCK_SUBBLOCKS (block);
+ for (tree b = BLOCK_SUBBLOCKS (replace_blk); b; b = BLOCK_CHAIN (b))
+ BLOCK_SUPERCONTEXT (b) = replace_blk;
+ }
+ BIND_EXPR_BLOCK (first) = replace_blk;
+ }
+
+ if (flag_exceptions)
+ {
+ tree ueh_meth
+ = lookup_promise_method (orig, coro_unhandled_exception_identifier,
+ fn_start, /*musthave=*/ true);
+ /* Build promise.unhandled_exception(); */
+ tree ueh
+ = build_new_method_call (p, ueh_meth, NULL, NULL_TREE, LOOKUP_NORMAL,
+ NULL, tf_warning_or_error);
+
+ /* The try block is just the original function, there's no real
+ need to call any function to do this. */
+ tree tcb = build_stmt (fn_start, TRY_BLOCK, NULL_TREE, NULL_TREE);
+ TRY_STMTS (tcb) = fnbody;
+ TRY_HANDLERS (tcb) = push_stmt_list ();
+ /* Mimic what the parser does for the catch. */
+ tree handler = begin_handler ();
+ finish_handler_parms (NULL_TREE, handler); /* catch (...) */
+ ueh = maybe_cleanup_point_expr_void (ueh);
+ add_stmt (ueh);
+ finish_handler (handler);
+ TRY_HANDLERS (tcb) = pop_stmt_list (TRY_HANDLERS (tcb));
+ /* If the function starts with a BIND_EXPR, then we need to create
+ one here to contain the try-catch and to link up the scopes. */
+ if (orig_fn_has_outer_bind)
+ {
+ tree tcb_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
+ /* Make and connect the scope blocks. */
+ tree tcb_block = make_node (BLOCK);
+ /* .. and connect it here. */
+ BLOCK_SUPERCONTEXT (replace_blk) = tcb_block;
+ BLOCK_SUBBLOCKS (tcb_block) = replace_blk;
+ BIND_EXPR_BLOCK (tcb_bind) = tcb_block;
+ BIND_EXPR_BODY (tcb_bind) = tcb;
+ fnbody = tcb_bind;
+ }
+ else
+ fnbody = tcb;
+ }
+ else if (pedantic)
+ {
+ /* We still try to look for the promise method and warn if it's not
+ present. */
+ tree ueh_meth
+ = lookup_promise_method (orig, coro_unhandled_exception_identifier,
+ fn_start, /*musthave=*/ false);
+ if (!ueh_meth || ueh_meth == error_mark_node)
+ warning_at (fn_start, 0, "no member named %qE in %qT",
+ coro_unhandled_exception_identifier,
+ get_coroutine_promise_type (orig));
+ }
+ /* Else we don't check and don't care if the method is missing. */
+
+ /* Start to build the final functions.
+
+ We push_deferring_access_checks to avoid these routines being seen as
+ nested by the middle end; we are doing the outlining here. */
+
+ push_deferring_access_checks (dk_no_check);
+
+ /* Actor ... */
+ build_actor_fn (fn_start, coro_frame_type, actor, fnbody, orig, param_uses,
+ &local_var_uses, param_dtor_list, initial_await, final_await,
+ body_aw_points.count);
+
+ /* Destroyer ... */
+ build_destroy_fn (fn_start, coro_frame_type, destroy, actor);
+
+ pop_deferring_access_checks ();
+
+ DECL_SAVED_TREE (orig) = newbody;
+ /* Link our new functions into the list. */
+ TREE_CHAIN (destroy) = TREE_CHAIN (orig);
+ TREE_CHAIN (actor) = destroy;
+ TREE_CHAIN (orig) = actor;
+
+ *resumer = actor;
+ *destroyer = destroy;
+
+ delete suspend_points;
+ suspend_points = NULL;
+ return true;
+}
+
+#include "gt-cp-coroutines.h"
+
MARK_TS_EXP (SIMPLE_REQ);
MARK_TS_EXP (TYPE_REQ);
+ MARK_TS_EXP (CO_AWAIT_EXPR);
+ MARK_TS_EXP (CO_YIELD_EXPR);
+ MARK_TS_EXP (CO_RETURN_EXPR);
+
c_common_init_ts ();
}
CHECK_CONSTR_ARGUMENTS are the template arguments */
DEFTREECODE (CHECK_CONSTR, "check_constr", tcc_expression, 2)
+/* The co_await expression is used to support coroutines.
+
+ Op 0 is the cast expresssion (potentially modified by the
+ promise "await_transform()" method).
+ Op1 is a proxy for the temp / coro frame slot 'e' value.
+ Op2 is the initialiser for Op1 (Op0, potentially modified by any
+ applicable 'co_await' operator).
+ Op3 is a vector of the [0] e.ready, [1] e.suspend and [2] e.resume calls.
+ Op4 is a mode : 0 (await) 1 (yield) 2 (initial) 3 (final) */
+DEFTREECODE (CO_AWAIT_EXPR, "co_await", tcc_expression, 5)
+
+/* The co_yield expression is used to support coroutines.
+
+ Op0 is the original expr (for use in diagnostics)
+ Op2 is the co_await derived from this. */
+DEFTREECODE (CO_YIELD_EXPR, "co_yield", tcc_expression, 2)
+
+/* The co_return expression is used to support coroutines.
+
+ Op0 is the original expr, can be void (for use in diagnostics)
+ Op2 is the promise return_xxxx call for Op0. */
+
+DEFTREECODE (CO_RETURN_EXPR, "co_return", tcc_expression, 2)
+
/*
Local variables:
mode:c
unsigned has_dependent_explicit_spec_p : 1;
unsigned immediate_fn_p : 1;
unsigned maybe_deleted : 1;
- unsigned spare : 10;
+ unsigned coroutine_p : 1;
+
+ unsigned spare : 9;
/* 32-bits padding on 64-bit host. */
#define QUALIFIED_NAME_IS_TEMPLATE(NODE) \
(TREE_LANG_FLAG_1 (SCOPE_REF_CHECK (NODE)))
+/* [coroutines]
+*/
+
+/* True if NODE is a co-routine FUNCTION_DECL. */
+#define DECL_COROUTINE_P(NODE) \
+ (LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->coroutine_p)
+
/* True for an OMP_ATOMIC that has dependent parameters. These are stored
as an expr in operand 1, and integer_zero_node or clauses in operand 0. */
#define OMP_ATOMIC_DEPENDENT_P(NODE) \
extern tree cp_ubsan_maybe_instrument_cast_to_vbase (location_t, tree, tree);
extern void cp_ubsan_maybe_initialize_vtbl_ptrs (tree);
+/* In coroutines.cc */
+extern tree finish_co_return_stmt (location_t, tree);
+extern tree finish_co_await_expr (location_t, tree);
+extern tree finish_co_yield_expr (location_t, tree);
+extern tree coro_validate_builtin_call (tree,
+ tsubst_flags_t = tf_warning_or_error);
+extern bool morph_fn_to_coro (tree, tree *, tree *);
+
/* Inline bodies. */
inline tree
indent);
}
+/* This function carries out the subset of finish_function operations needed
+ to emit the compiler-generated outlined helper functions used by the
+ coroutines implementation. */
+
+static void
+emit_coro_helper (tree helper)
+{
+ /* This is a partial set of the operations done by finish_function()
+ plus emitting the result. */
+ set_cfun (NULL);
+ current_function_decl = helper;
+ begin_scope (sk_function_parms, NULL);
+ store_parm_decls (DECL_ARGUMENTS (helper));
+ announce_function (helper);
+ allocate_struct_function (helper, false);
+ cfun->language = ggc_cleared_alloc<language_function> ();
+ poplevel (1, 0, 1);
+ maybe_save_function_definition (helper);
+ /* We must start each function with a clear fold cache. */
+ clear_fold_cache ();
+ cp_fold_function (helper);
+ DECL_CONTEXT (DECL_RESULT (helper)) = helper;
+ BLOCK_SUPERCONTEXT (DECL_INITIAL (helper)) = helper;
+ /* This function has coroutine IFNs that we should handle in middle
+ end lowering. */
+ cfun->coroutine_component = true;
+ cp_genericize (helper);
+ expand_or_defer_fn (helper);
+}
+
/* Finish up a function declaration and compile that function
all the way to assembler language output. The free the storage
for the function definition. INLINE_P is TRUE if we just
{
tree fndecl = current_function_decl;
tree fntype, ctype = NULL_TREE;
+ tree resumer = NULL_TREE, destroyer = NULL_TREE;
+ bool coro_p = flag_coroutines
+ && !processing_template_decl
+ && DECL_COROUTINE_P (fndecl);
/* When we get some parse errors, we can end up without a
current_function_decl, so cope. */
error_mark_node. */
gcc_assert (DECL_INITIAL (fndecl) == error_mark_node);
+ if (coro_p)
+ {
+ if (!morph_fn_to_coro (fndecl, &resumer, &destroyer))
+ {
+ DECL_SAVED_TREE (fndecl) = pop_stmt_list (DECL_SAVED_TREE (fndecl));
+ poplevel (1, 0, 1);
+ DECL_SAVED_TREE (fndecl) = error_mark_node;
+ return fndecl;
+ }
+
+ /* We should handle coroutine IFNs in middle end lowering. */
+ cfun->coroutine_component = true;
+
+ if (use_eh_spec_block (fndecl))
+ finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS
+ (TREE_TYPE (fndecl)),
+ current_eh_spec_block);
+ }
+ else
/* For a cloned function, we've already got all the code we need;
there's no need to add any extra bits. */
if (!DECL_CLONED_FUNCTION_P (fndecl))
&& !DECL_OMP_DECLARE_REDUCTION_P (fndecl))
cp_genericize (fndecl);
+ /* Emit the resumer and destroyer functions now. */
+ if (coro_p)
+ {
+ emit_coro_helper (resumer);
+ emit_coro_helper (destroyer);
+ }
+
cleanup:
/* We're leaving the context of this function, so zap cfun. It's still in
DECL_STRUCT_FUNCTION, and we'll restore it in tree_rest_of_compilation. */
mask |= D_CXX20;
if (!flag_concepts)
mask |= D_CXX_CONCEPTS;
+ if (!flag_coroutines)
+ mask |= D_CXX_COROUTINES;
if (!flag_tm)
mask |= D_TRANSMEM;
if (!flag_char8_t)
DEF_OPERATOR ("--", PREDECREMENT_EXPR, "mm", OVL_OP_FLAG_UNARY)
DEF_OPERATOR ("->", COMPONENT_REF, "pt", OVL_OP_FLAG_UNARY)
DEF_OPERATOR ("sizeof", SIZEOF_EXPR, "sz", OVL_OP_FLAG_UNARY)
+DEF_OPERATOR ("co_await", CO_AWAIT_EXPR, "aw", OVL_OP_FLAG_UNARY)
/* These are extensions. */
DEF_OPERATOR ("alignof", ALIGNOF_EXPR, "az", OVL_OP_FLAG_UNARY)
RT_CLASS_TYPENAME_TEMPLATE, /* class, typename, or template */
RT_TRANSACTION_ATOMIC, /* __transaction_atomic */
RT_TRANSACTION_RELAXED, /* __transaction_relaxed */
- RT_TRANSACTION_CANCEL /* __transaction_cancel */
+ RT_TRANSACTION_CANCEL, /* __transaction_cancel */
+
+ RT_CO_YIELD /* co_yield */
};
/* RAII wrapper for parser->in_type_id_in_expr_p, setting it on creation and
static tree cp_parser_transaction_cancel
(cp_parser *);
+/* Coroutine extensions. */
+
+static tree cp_parser_yield_expression
+ (cp_parser *);
+
+
enum pragma_context {
pragma_external,
pragma_member,
postfix-expression
++ cast-expression
-- cast-expression
+ await-expression
unary-operator cast-expression
sizeof unary-expression
sizeof ( type-id )
noexcept_loc);
}
+ case RID_CO_AWAIT:
+ {
+ tree expr;
+ location_t kw_loc = token->location;
+
+ /* Consume the `co_await' token. */
+ cp_lexer_consume_token (parser->lexer);
+ /* Parse its cast-expression. */
+ expr = cp_parser_simple_cast_expression (parser);
+ if (expr == error_mark_node)
+ return error_mark_node;
+
+ /* Handle [expr.await]. */
+ return cp_expr (finish_co_await_expr (kw_loc, expr));
+ }
+
default:
break;
}
conditional-expression
logical-or-expression assignment-operator assignment_expression
throw-expression
+ yield-expression
CAST_P is true if this expression is the target of a cast.
DECLTYPE_P is true if this expression is the operand of decltype.
a throw-expression. */
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_THROW))
expr = cp_parser_throw_expression (parser);
+ /* If the next token is the `co_yield' keyword, then we're looking at
+ a yield-expression. */
+ else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CO_YIELD))
+ expr = cp_parser_yield_expression (parser);
/* Otherwise, it must be that we are looking at a
logical-or-expression. */
else
case RID_BREAK:
case RID_CONTINUE:
case RID_RETURN:
+ case RID_CO_RETURN:
case RID_GOTO:
std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc);
statement = cp_parser_jump_statement (parser);
continue ;
return expression [opt] ;
return braced-init-list ;
+ coroutine-return-statement;
goto identifier ;
GNU extension:
cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
break;
+ case RID_CO_RETURN:
case RID_RETURN:
{
tree expr;
/* If the next token is a `;', then there is no
expression. */
expr = NULL_TREE;
- /* Build the return-statement. */
- if (FNDECL_USED_AUTO (current_function_decl) && in_discarded_stmt)
+ /* Build the return-statement, check co-return first, since type
+ deduction is not valid there. */
+ if (keyword == RID_CO_RETURN)
+ statement = finish_co_return_stmt (token->location, expr);
+ else if (FNDECL_USED_AUTO (current_function_decl) && in_discarded_stmt)
/* Don't deduce from a discarded return statement. */;
else
statement = finish_return_stmt (expr);
{
case CPP_KEYWORD:
{
- /* The keyword should be either `new' or `delete'. */
+ /* The keyword should be either `new', `delete' or `co_await'. */
if (token->keyword == RID_NEW)
op = NEW_EXPR;
else if (token->keyword == RID_DELETE)
op = DELETE_EXPR;
+ else if (token->keyword == RID_CO_AWAIT)
+ op = CO_AWAIT_EXPR;
else
break;
- /* Consume the `new' or `delete' token. */
+ /* Consume the `new', `delete' or co_await token. */
end_loc = cp_lexer_consume_token (parser->lexer)->location;
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
/* If it's a `[' token then this is the array variant of the
operator. */
- if (token->type == CPP_OPEN_SQUARE)
+ if (token->type == CPP_OPEN_SQUARE
+ && op != CO_AWAIT_EXPR)
{
/* Consume the `[' token. */
cp_lexer_consume_token (parser->lexer);
return expression;
}
+/* Parse a yield-expression.
+
+ yield-expression:
+ co_yield assignment-expression
+ co_yield braced-init-list
+
+ Returns a CO_YIELD_EXPR representing the yield-expression. */
+
+static tree
+cp_parser_yield_expression (cp_parser* parser)
+{
+ tree expr;
+
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ location_t kw_loc = token->location; /* Save for later. */
+
+ cp_parser_require_keyword (parser, RID_CO_YIELD, RT_CO_YIELD);
+
+ if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+ {
+ bool expr_non_constant_p;
+ cp_lexer_set_source_position (parser->lexer);
+ /* ??? : probably a moot point? */
+ maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
+ expr = cp_parser_braced_list (parser, &expr_non_constant_p);
+ }
+ else
+ expr = cp_parser_assignment_expression (parser);
+
+ if (expr == error_mark_node)
+ return expr;
+
+ return finish_co_yield_expr (kw_loc, expr);
+}
+
/* GNU Extensions */
/* Parse an (optional) asm-specification.
case RT_TRANSACTION_RELAXED:
gmsgid = G_("expected %<__transaction_relaxed%>");
break;
+ case RT_CO_YIELD:
+ gmsgid = G_("expected %<co_yield%>");
+ break;
default:
break;
}
to the containing function, inlined copy or so. */
return t;
+ case CO_AWAIT_EXPR:
+ return tsubst_expr (t, args, complain, in_decl,
+ /*integral_constant_expression_p=*/false);
+ break;
+
default:
/* We shouldn't get here, but keep going if !flag_checking. */
if (flag_checking)
finish_return_stmt (RECUR (TREE_OPERAND (t, 0)));
break;
+ case CO_RETURN_EXPR:
+ finish_co_return_stmt (input_location, RECUR (TREE_OPERAND (t, 0)));
+ break;
+
+ case CO_YIELD_EXPR:
+ stmt = finish_co_yield_expr (input_location,
+ RECUR (TREE_OPERAND (t, 0)));
+ RETURN (stmt);
+ break;
+
+ case CO_AWAIT_EXPR:
+ stmt = finish_co_await_expr (input_location,
+ RECUR (TREE_OPERAND (t, 0)));
+ RETURN (stmt);
+ break;
+
case EXPR_STMT:
tmp = RECUR (EXPR_STMT_EXPR (t));
if (EXPR_STMT_STMT_EXPR_RESULT (t))
WALK_SUBTREE (TREE_VALUE (cap));
break;
+ case CO_YIELD_EXPR:
+ if (TREE_OPERAND (*tp, 1))
+ /* Operand 1 is the tree for the relevant co_await which has any
+ interesting sub-trees. */
+ WALK_SUBTREE (TREE_OPERAND (*tp, 1));
+ break;
+
+ case CO_AWAIT_EXPR:
+ if (TREE_OPERAND (*tp, 1))
+ /* Operand 1 is frame variable. */
+ WALK_SUBTREE (TREE_OPERAND (*tp, 1));
+ if (TREE_OPERAND (*tp, 2))
+ /* Operand 2 has the initialiser, and we need to walk any subtrees
+ there. */
+ WALK_SUBTREE (TREE_OPERAND (*tp, 2));
+ break;
+
+ case CO_RETURN_EXPR:
+ if (TREE_OPERAND (*tp, 0))
+ {
+ if (VOID_TYPE_P (TREE_OPERAND (*tp, 0)))
+ /* For void expressions, operand 1 is a trivial call, and any
+ interesting subtrees will be part of operand 0. */
+ WALK_SUBTREE (TREE_OPERAND (*tp, 0));
+ else if (TREE_OPERAND (*tp, 1))
+ /* Interesting sub-trees will be in the return_value () call
+ arguments. */
+ WALK_SUBTREE (TREE_OPERAND (*tp, 1));
+ }
+ break;
+
default:
return NULL_TREE;
}
evaluation might take too long.
The default is 33554432 (1<<25).
+@item -fcoroutines
+@opindex fcoroutines
+Enable support for the C++ coroutines extension (experimental).
+
@item -fno-elide-constructors
@opindex fno-elide-constructors
@opindex felide-constructors
/* Set when the function was compiled with generation of debug
(begin stmt, inline entry, ...) markers enabled. */
unsigned int debug_nonbind_markers : 1;
+
+ /* Set if this is a coroutine-related function. */
+ unsigned int coroutine_component : 1;
};
/* Add the decl D to the local_decls list of FUN. */
/* Nothing. But it shouldn't really prevail. */
}
+/* Coroutines, all should have been processed at this stage. */
+
+static void
+expand_CO_FRAME (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+static void
+expand_CO_YIELD (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+static void
+expand_CO_SUSPN (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+static void
+expand_CO_ACTOR (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
/* Expand a call to FN using the operands in STMT. FN has a single
output operand and NARGS input operands. */
/* Divmod function. */
DEF_INTERNAL_FN (DIVMOD, ECF_CONST | ECF_LEAF, NULL)
+/* For coroutines. */
+DEF_INTERNAL_FN (CO_ACTOR, ECF_NOTHROW | ECF_LEAF, NULL)
+DEF_INTERNAL_FN (CO_YIELD, ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (CO_SUSPN, ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (CO_FRAME, ECF_PURE | ECF_NOTHROW | ECF_LEAF, NULL)
+
/* A NOP function with arbitrary arguments and return value. */
DEF_INTERNAL_FN (NOP, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
NEXT_PASS (pass_lower_tm);
NEXT_PASS (pass_refactor_eh);
NEXT_PASS (pass_lower_eh);
+ NEXT_PASS (pass_coroutine_lower_builtins);
NEXT_PASS (pass_build_cfg);
NEXT_PASS (pass_warn_function_return);
+ NEXT_PASS (pass_coroutine_early_expand_ifns);
NEXT_PASS (pass_expand_omp);
NEXT_PASS (pass_warn_printf);
NEXT_PASS (pass_walloca, /*strict_mode_p=*/true);
+2020-01-18 Iain Sandoe <iain@sandoe.co.uk>
+
+ * g++.dg/coroutines/co-await-syntax-00-needs-expr.C: New test.
+ * g++.dg/coroutines/co-await-syntax-01-outside-fn.C: New test.
+ * g++.dg/coroutines/co-await-syntax-02-outside-fn.C: New test.
+ * g++.dg/coroutines/co-await-syntax-03-auto.C: New test.
+ * g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C: New test.
+ * g++.dg/coroutines/co-await-syntax-05-constexpr.C: New test.
+ * g++.dg/coroutines/co-await-syntax-06-main.C: New test.
+ * g++.dg/coroutines/co-await-syntax-07-varargs.C: New test.
+ * g++.dg/coroutines/co-await-syntax-08-lambda-auto.C: New test.
+ * g++.dg/coroutines/co-return-syntax-01-outside-fn.C: New test.
+ * g++.dg/coroutines/co-return-syntax-02-outside-fn.C: New test.
+ * g++.dg/coroutines/co-return-syntax-03-auto.C: New test.
+ * g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C: New test.
+ * g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C: New test.
+ * g++.dg/coroutines/co-return-syntax-06-main.C: New test.
+ * g++.dg/coroutines/co-return-syntax-07-vararg.C: New test.
+ * g++.dg/coroutines/co-return-syntax-08-bad-return.C: New test.
+ * g++.dg/coroutines/co-return-syntax-09-lambda-auto.C: New test.
+ * g++.dg/coroutines/co-yield-syntax-00-needs-expr.C: New test.
+ * g++.dg/coroutines/co-yield-syntax-01-outside-fn.C: New test.
+ * g++.dg/coroutines/co-yield-syntax-02-outside-fn.C: New test.
+ * g++.dg/coroutines/co-yield-syntax-03-auto.C: New test.
+ * g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C: New test.
+ * g++.dg/coroutines/co-yield-syntax-05-constexpr.C: New test.
+ * g++.dg/coroutines/co-yield-syntax-06-main.C: New test.
+ * g++.dg/coroutines/co-yield-syntax-07-varargs.C: New test.
+ * g++.dg/coroutines/co-yield-syntax-08-needs-expr.C: New test.
+ * g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C: New test.
+ * g++.dg/coroutines/coro-builtins.C: New test.
+ * g++.dg/coroutines/coro-missing-gro.C: New test.
+ * g++.dg/coroutines/coro-missing-promise-yield.C: New test.
+ * g++.dg/coroutines/coro-missing-ret-value.C: New test.
+ * g++.dg/coroutines/coro-missing-ret-void.C: New test.
+ * g++.dg/coroutines/coro-missing-ueh-1.C: New test.
+ * g++.dg/coroutines/coro-missing-ueh-2.C: New test.
+ * g++.dg/coroutines/coro-missing-ueh-3.C: New test.
+ * g++.dg/coroutines/coro-missing-ueh.h: New test.
+ * g++.dg/coroutines/coro-pre-proc.C: New test.
+ * g++.dg/coroutines/coro.h: New file.
+ * g++.dg/coroutines/coro1-ret-int-yield-int.h: New file.
+ * g++.dg/coroutines/coroutines.exp: New file.
+ * g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C: New test.
+ * g++.dg/coroutines/torture/alloc-01-overload-newdel.C: New test.
+ * g++.dg/coroutines/torture/call-00-co-aw-arg.C: New test.
+ * g++.dg/coroutines/torture/call-01-multiple-co-aw.C: New test.
+ * g++.dg/coroutines/torture/call-02-temp-co-aw.C: New test.
+ * g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C: New test.
+ * g++.dg/coroutines/torture/class-00-co-ret.C: New test.
+ * g++.dg/coroutines/torture/class-01-co-ret-parm.C: New test.
+ * g++.dg/coroutines/torture/class-02-templ-parm.C: New test.
+ * g++.dg/coroutines/torture/class-03-operator-templ-parm.C: New test.
+ * g++.dg/coroutines/torture/class-04-lambda-1.C: New test.
+ * g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C: New test.
+ * g++.dg/coroutines/torture/class-06-lambda-capture-ref.C: New test.
+ * g++.dg/coroutines/torture/co-await-00-trivial.C: New test.
+ * g++.dg/coroutines/torture/co-await-01-with-value.C: New test.
+ * g++.dg/coroutines/torture/co-await-02-xform.C: New test.
+ * g++.dg/coroutines/torture/co-await-03-rhs-op.C: New test.
+ * g++.dg/coroutines/torture/co-await-04-control-flow.C: New test.
+ * g++.dg/coroutines/torture/co-await-05-loop.C: New test.
+ * g++.dg/coroutines/torture/co-await-06-ovl.C: New test.
+ * g++.dg/coroutines/torture/co-await-07-tmpl.C: New test.
+ * g++.dg/coroutines/torture/co-await-08-cascade.C: New test.
+ * g++.dg/coroutines/torture/co-await-09-pair.C: New test.
+ * g++.dg/coroutines/torture/co-await-10-template-fn-arg.C: New test.
+ * g++.dg/coroutines/torture/co-await-11-forwarding.C: New test.
+ * g++.dg/coroutines/torture/co-await-12-operator-2.C: New test.
+ * g++.dg/coroutines/torture/co-await-13-return-ref.C: New test.
+ * g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C: New test.
+ * g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C: New test.
+ * g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C: New test.
+ * g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C: New test.
+ * g++.dg/coroutines/torture/co-ret-05-return-value.C: New test.
+ * g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C: New test.
+ * g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C: New test.
+ * g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C: New test.
+ * g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C: New test.
+ * g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C: New test.
+ * g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C: New test.
+ * g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C: New test.
+ * g++.dg/coroutines/torture/co-ret-13-template-2.C: New test.
+ * g++.dg/coroutines/torture/co-ret-14-template-3.C: New test.
+ * g++.dg/coroutines/torture/co-yield-00-triv.C: New test.
+ * g++.dg/coroutines/torture/co-yield-01-multi.C: New test.
+ * g++.dg/coroutines/torture/co-yield-02-loop.C: New test.
+ * g++.dg/coroutines/torture/co-yield-03-tmpl.C: New test.
+ * g++.dg/coroutines/torture/co-yield-04-complex-local-state.C: New test.
+ * g++.dg/coroutines/torture/co-yield-05-co-aw.C: New test.
+ * g++.dg/coroutines/torture/co-yield-06-fun-parm.C: New test.
+ * g++.dg/coroutines/torture/co-yield-07-template-fn-param.C: New test.
+ * g++.dg/coroutines/torture/co-yield-08-more-refs.C: New test.
+ * g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C: New test.
+ * g++.dg/coroutines/torture/coro-torture.exp: New file.
+ * g++.dg/coroutines/torture/exceptions-test-0.C: New test.
+ * g++.dg/coroutines/torture/func-params-00.C: New test.
+ * g++.dg/coroutines/torture/func-params-01.C: New test.
+ * g++.dg/coroutines/torture/func-params-02.C: New test.
+ * g++.dg/coroutines/torture/func-params-03.C: New test.
+ * g++.dg/coroutines/torture/func-params-04.C: New test.
+ * g++.dg/coroutines/torture/func-params-05.C: New test.
+ * g++.dg/coroutines/torture/func-params-06.C: New test.
+ * g++.dg/coroutines/torture/lambda-00-co-ret.C: New test.
+ * g++.dg/coroutines/torture/lambda-01-co-ret-parm.C: New test.
+ * g++.dg/coroutines/torture/lambda-02-co-yield-values.C: New test.
+ * g++.dg/coroutines/torture/lambda-03-auto-parm-1.C: New test.
+ * g++.dg/coroutines/torture/lambda-04-templ-parm.C: New test.
+ * g++.dg/coroutines/torture/lambda-05-capture-copy-local.C: New test.
+ * g++.dg/coroutines/torture/lambda-06-multi-capture.C: New test.
+ * g++.dg/coroutines/torture/lambda-07-multi-yield.C: New test.
+ * g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C: New test.
+ * g++.dg/coroutines/torture/local-var-0.C: New test.
+ * g++.dg/coroutines/torture/local-var-1.C: New test.
+ * g++.dg/coroutines/torture/local-var-2.C: New test.
+ * g++.dg/coroutines/torture/local-var-3.C: New test.
+ * g++.dg/coroutines/torture/local-var-4.C: New test.
+ * g++.dg/coroutines/torture/mid-suspend-destruction-0.C: New test.
+ * g++.dg/coroutines/torture/pr92933.C: New test.
+
2020-01-17 Jerry DeLisle <jvdelisle@gcc.gnu.org>
PR libfortran/93234
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+void bar () {
+ co_await; // { dg-error "expected primary-expression before" }
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int x = co_await coro::suspend_always{}; // { dg-error {'co_await' cannot be used outside a function} }
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+auto f (int x = co_await coro::suspend_always{}); // { dg-error {'co_await' cannot be used outside a function} }
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+extern struct awaitable *aw ();
+
+auto bar () {
+ int x = 1 + co_await *aw(); // { dg-error "cannot be used in a function with a deduced return type" }
+
+ return x;
+}
+
+int main () {
+ bar ();
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+struct Foo {
+ Foo () { co_await coro::suspend_always{}; } // { dg-error "cannot be used in a constructor" }
+ ~Foo () { co_await coro::suspend_always{}; } // { dg-error "cannot be used in a destructor" }
+};
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+constexpr int bar () {
+ co_await coro::suspend_always{}; // { dg-error "cannot be used in a .constexpr. function" }
+ return 42; /* Suppress the "no return" error. */
+}
+
+int main () {
+ return bar ();
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int main (int ac, char *av[]) {
+ co_await coro::suspend_always{}; // { dg-error "cannot be used in the .main. function" }
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int
+bar (int x, ...)
+{
+ co_await coro::suspend_always{}; // { dg-error "cannot be used in a varargs function" }
+}
+
+int main (int ac, char *av[]) {
+ bar (5, ac);
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+// Check that we decline return type deduction for lambda coroutines.
+
+#include "coro.h"
+
+// boiler-plate for tests of codegen
+#include "coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ /* Attempt to deduce the return type for a lambda coroutine. */
+ auto f = []()
+ {
+ co_await coro::suspend_always{}; // { dg-error "cannot be used in a function with a deduced return type" }
+ };
+
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+co_return; // { dg-error {expected unqualified-id before 'co_return'} }
+
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+auto f (co_return); // { dg-error {expected primary-expression before 'co_return'} }
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+auto bar () {
+ co_return 5; // { dg-error "cannot be used in a function with a deduced return type" }
+}
+
+int main () {
+ bar ();
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+struct Foo {
+ Foo () { co_return; } // { dg-error "cannot be used in a constructor" }
+ ~Foo () { co_return 5; } // { dg-error "cannot be used in a destructor" }
+};
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+constexpr int bar () {
+ co_return 5; // { dg-error "cannot be used in a .constexpr. function" }
+ return 42; /* Suppress the "no return" error. */
+}
+
+int main () {
+ return bar ();
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int main (int ac, char *av[]) {
+ co_return 0; // { dg-error "cannot be used in the .main. function" }
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int
+bar (int x, ...)
+{
+ co_return 1; // { dg-error "cannot be used in a varargs function" }
+}
+
+int main (int ac, char *av[]) {
+ bar (5, ac);
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+struct Coro {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<Coro::promise_type>;
+ handle_type handle;
+ Coro () : handle(0) {}
+ Coro (handle_type _handle) : handle(_handle) {}
+ Coro (Coro &&s) : handle(s.handle) { s.handle = nullptr; }
+ Coro &operator = (Coro &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ Coro (const Coro &) = delete;
+ ~Coro() {
+ if ( handle )
+ handle.destroy();
+ }
+ struct promise_type {
+ promise_type() {}
+ ~promise_type() {}
+ Coro get_return_object () { return Coro (handle_type::from_promise (*this)); }
+ auto initial_suspend () { return coro::suspend_always{}; }
+ auto final_suspend () { return coro::suspend_always{}; }
+ void return_void () { }
+ void unhandled_exception() { }
+ };
+};
+
+extern int x;
+
+// Diagnose disallowed "return" in coroutine.
+Coro
+bar () // { dg-error {a 'return' statement is not allowed} }
+{
+ if (x)
+ return Coro();
+ else
+ co_return;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+// Check that we decline return type deduction for lambda coroutines.
+
+#include "coro.h"
+
+// boiler-plate for tests of codegen
+#include "coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ /* Attempt to deduce the return type for a lambda coroutine. */
+ auto f = []()
+ {
+ co_return 42; // { dg-error "cannot be used in a function with a deduced return type" }
+ };
+
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+void foo () {
+ co_yield; // { dg-error "expected primary-expression before" }
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+auto f (int x = co_yield 5); // { dg-error {'co_yield' cannot be used outside a function} }
+
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int a[] = { co_yield 21 }; // { dg-error {'co_yield' cannot be used outside a function} }
+
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+auto bar () {
+ co_yield 5; // { dg-error "cannot be used in a function with a deduced return type" }
+}
+
+int main () {
+ bar ();
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+struct Foo {
+ Foo () { co_yield 4; } // { dg-error "cannot be used in a constructor" }
+ ~Foo () { co_yield 4; } // { dg-error "cannot be used in a destructor" }
+};
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+constexpr int bar () {
+ co_yield 5; // { dg-error "cannot be used in a .constexpr. function" }
+ return 42; /* Suppress the "no return" error. */
+}
+
+int main () {
+ return bar ();
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int main (int ac, char *av[]) {
+ co_yield 0; // { dg-error "cannot be used in the .main. function" }
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int
+bar (int x, ...)
+{
+ co_yield 1; // { dg-error "cannot be used in a varargs function" }
+}
+
+int main (int ac, char *av[]) {
+ bar (5, ac);
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+// Check syntax for missing expr in a coroutine context.
+
+#include "coro.h"
+
+struct DummyYield {
+ coro::coroutine_handle<> handle;
+ DummyYield () : handle (nullptr) {}
+ DummyYield (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct dummy_yield {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ DummyYield get_return_object() {
+ return DummyYield (coro::coroutine_handle<dummy_yield>::from_promise (*this));
+ }
+ void yield_value (int v) {}
+ void return_value (int v) {}
+ void unhandled_exception() { /*std::terminate();*/ };
+ };
+};
+
+template<> struct coro::coroutine_traits<DummyYield> {
+ using promise_type = DummyYield::dummy_yield;
+};
+
+DummyYield
+bar ()
+{
+ co_yield; // { dg-error {expected primary-expression before} }
+ co_return 0;
+}
+
+int main (int ac, char *av[]) {
+ DummyYield x = bar ();
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+// Check that we decline return type deduction for lambda coroutines.
+
+#include "coro.h"
+
+// boiler-plate for tests of codegen
+#include "coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ /* Attempt to deduce the return type for a lambda coroutine. */
+ auto f = []()
+ {
+ co_yield 42; // { dg-error "cannot be used in a function with a deduced return type" }
+ };
+
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only " }
+
+typedef __SIZE_TYPE__ size_t;
+
+int main ()
+{
+ void *co_h;
+ void *promise;
+ const size_t co_align = 16;
+
+ bool d = __builtin_coro_done (co_h);
+ __builtin_coro_resume (co_h);
+ __builtin_coro_destroy (co_h);
+ promise = __builtin_coro_promise (co_h, co_align, true);
+
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+// Diagose missing get_return_object() in the promise type.
+
+#include "coro.h"
+
+struct MissingGRO {
+ coro::coroutine_handle<> handle;
+ MissingGRO () : handle (nullptr) {}
+ MissingGRO (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct missing_gro {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ void return_void () {}
+ void unhandled_exception() { /*std::terminate();*/ };
+ };
+};
+
+template<> struct coro::coroutine_traits<MissingGRO> {
+ using promise_type = MissingGRO::missing_gro;
+};
+
+MissingGRO
+bar () // { dg-error {no member named 'get_return_object' in} }
+{
+ co_return;
+}
+
+int main (int ac, char *av[]) {
+ MissingGRO x = bar ();
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+struct MissingPromiseYield {
+ coro::coroutine_handle<> handle;
+ MissingPromiseYield () : handle (nullptr) {}
+ MissingPromiseYield (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct missing_yield {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ MissingPromiseYield get_return_object() {
+ return MissingPromiseYield (coro::coroutine_handle<missing_yield>::from_promise (*this));
+ }
+ void return_value (int v) {}
+ void unhandled_exception() { /*std::terminate();*/ };
+ };
+};
+
+template<> struct coro::coroutine_traits<MissingPromiseYield> {
+ using promise_type = MissingPromiseYield::missing_yield;
+};
+
+MissingPromiseYield
+bar ()
+{
+ co_yield 22; // { dg-error {no member named 'yield_value' in} }
+ co_return 0;
+}
+
+int main (int ac, char *av[]) {
+ MissingPromiseYield x = bar ();
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+// Diagose missing return_value() in the promise type.
+
+#include "coro.h"
+
+struct MissingRetValue {
+ coro::coroutine_handle<> handle;
+ MissingRetValue () : handle (nullptr) {}
+ MissingRetValue (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct missing_retvoid {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ MissingRetValue get_return_object() {
+ return MissingRetValue (coro::coroutine_handle<missing_retvoid>::from_promise (*this));
+ }
+ void unhandled_exception() { /*std::terminate();*/ };
+ };
+};
+
+template<> struct coro::coroutine_traits<MissingRetValue> {
+ using promise_type = MissingRetValue::missing_retvoid;
+};
+
+MissingRetValue
+bar ()
+{
+ co_return 6174; // { dg-error {no member named 'return_value' in} }
+}
+
+int main (int ac, char *av[]) {
+ MissingRetValue x = bar ();
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+// Diagose missing return_void() in the promise type.
+
+struct MissingRetVoid {
+ coro::coroutine_handle<> handle;
+ MissingRetVoid () : handle (nullptr) {}
+ MissingRetVoid (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct missing_retvoid {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ MissingRetVoid get_return_object() {
+ return MissingRetVoid (coro::coroutine_handle<missing_retvoid>::from_promise (*this));
+ }
+ void unhandled_exception() { /*std::terminate();*/ };
+ };
+};
+
+template<> struct coro::coroutine_traits<MissingRetVoid> {
+ using promise_type = MissingRetVoid::missing_retvoid;
+};
+
+MissingRetVoid
+bar ()
+{
+ co_return; // { dg-error "no member named .return_void. in" }
+}
+
+int main (int ac, char *av[]) {
+ MissingRetVoid x = bar ();
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -fexceptions -w" }
+
+// Diagose missing unhandled_exception() in the promise type.
+
+#include "coro.h"
+#include "coro-missing-ueh.h"
+
+MissingUEH
+bar () // { dg-error {no member named 'unhandled_exception' in} }
+{
+ co_return;
+}
+
+int main (int ac, char *av[]) {
+ MissingUEH x = bar ();
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -fno-exceptions " }
+
+// The missing method is warned for when exceptions are off and pedantic
+// is on (default in the testsuite).
+
+#include "coro.h"
+#include "coro-missing-ueh.h"
+
+MissingUEH
+bar () // { dg-warning {no member named 'unhandled_exception' in} }
+{
+ co_return;
+}
+
+int main (int ac, char *av[]) {
+ MissingUEH x = bar ();
+ return 0;
+}
--- /dev/null
+// { dg-additional-options "-fsyntax-only -fno-exceptions -Wno-pedantic" }
+
+/* We don't warn about the missing method, unless in pedantic mode, so
+ this compile should be clean. */
+
+#include "coro.h"
+#include "coro-missing-ueh.h"
+
+MissingUEH
+bar ()
+{
+ co_return;
+}
+
+int main (int ac, char *av[]) {
+ MissingUEH x = bar ();
+ return 0;
+}
--- /dev/null
+#ifndef __MissingUEH_H
+#define __MissingUEH_H
+
+/* Common code for testing missing unhandled_exception. */
+struct MissingUEH {
+ coro::coroutine_handle<> handle;
+ MissingUEH () : handle (nullptr) {}
+ MissingUEH (coro::coroutine_handle<> handle) : handle (handle) {}
+ struct missing_ueh {
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ MissingUEH get_return_object() {
+ return MissingUEH (coro::coroutine_handle<missing_ueh>::from_promise (*this));
+ }
+ void return_void () {}
+ };
+};
+
+template<> struct coro::coroutine_traits<MissingUEH> {
+ using promise_type = MissingUEH::missing_ueh;
+};
+
+#endif
--- /dev/null
+// Only need to compile this, with the default options from the .exp.
+
+#ifndef __cpp_coroutines
+#error "coroutines should engaged."
+#endif
+
+#if __cpp_coroutines != 201902L
+#error "coroutine version out of sync."
+#endif
--- /dev/null
+#if __has_include(<coroutine>)
+
+#include <coroutine>
+
+# if __clang__
+# include <utility>
+# endif
+
+namespace coro = std;
+
+#elif __has_include(<experimental/coroutine>)
+
+#include <experimental/coroutine>
+
+# if __clang__
+# include <utility>
+# endif
+
+namespace coro = std::experimental;
+
+#else
+
+#warning "no installed coroutine headers found, using test-suite local one"
+
+/* Dummy version to allow tests without an installed header. */
+# ifndef __TESTSUITE_CORO_H_n4835
+# define __TESTSUITE_CORO_H_n4835
+
+// Fragments (with short-cuts) to mimic enough of the library header to
+// make some progress.
+
+# if __cpp_coroutines
+
+namespace std {
+inline namespace __n4835 {
+
+// 21.11.1 coroutine traits
+template<typename _R, typename...> struct coroutine_traits {
+ using promise_type = typename _R::promise_type;
+};
+
+// 21.11.2 coroutine handle
+template <typename Promise = void> struct coroutine_handle;
+
+template <>
+struct coroutine_handle<void> {
+ public:
+ // 21.11.2.1 construct/reset
+ constexpr coroutine_handle () noexcept
+ : __fr_ptr (0) {}
+ constexpr coroutine_handle (decltype(nullptr) __h) noexcept
+ : __fr_ptr (__h) {}
+ coroutine_handle &operator= (decltype(nullptr)) noexcept {
+ __fr_ptr = nullptr;
+ return *this;
+ }
+
+ public:
+ // 21.11.2.2 export/import
+ constexpr void *address () const noexcept { return __fr_ptr; }
+ constexpr static coroutine_handle from_address (void *__a) noexcept {
+ coroutine_handle __self;
+ __self.__fr_ptr = __a;
+ return __self;
+ }
+ public:
+ // 21.11.2.3 observers
+ constexpr explicit operator bool () const noexcept {
+ return bool (__fr_ptr);
+ }
+ bool done () const noexcept {
+ return __builtin_coro_done (__fr_ptr);
+ }
+ // 21.11.2.4 resumption
+ void operator () () const { resume (); }
+ void resume () const {
+ __builtin_coro_resume (__fr_ptr);
+ }
+ void destroy () const {
+ __builtin_coro_destroy (__fr_ptr);
+ }
+ protected:
+ void *__fr_ptr;
+};
+
+template <class _Promise>
+struct coroutine_handle : coroutine_handle<> {
+ // 21.11.2.1 construct/reset
+ using coroutine_handle<>::coroutine_handle;
+ static coroutine_handle from_promise(_Promise &p) {
+ coroutine_handle __self;
+ __self.__fr_ptr =
+ __builtin_coro_promise((char *)&p, __alignof(_Promise), true);
+ return __self;
+ }
+ coroutine_handle& operator=(decltype(nullptr)) noexcept {
+ coroutine_handle<>::operator=(nullptr);
+ return *this;
+ }
+ // 21.11.2.2 export/import
+ constexpr static coroutine_handle from_address(void* __a){
+ coroutine_handle __self;
+ __self.__fr_ptr = __a;
+ return __self;
+ }
+ // 21.11.2.5 promise access
+ _Promise& promise() const {
+ void * __t = __builtin_coro_promise(this->__fr_ptr,
+ __alignof(_Promise), false);
+ return *static_cast<_Promise*>(__t);
+ }
+};
+
+// n4760 - 21.11.5 trivial awaitables
+
+struct suspend_always {
+ bool await_ready() { return false; }
+ void await_suspend(coroutine_handle<>) {}
+ void await_resume() {}
+};
+
+struct suspend_never {
+ bool await_ready() { return true; }
+ void await_suspend(coroutine_handle<>) {}
+ void await_resume() {}
+};
+
+} // namespace __n4835
+} // namespace std
+
+namespace coro = std;
+
+# else
+# error "coro.h requires support for coroutines, add -fcoroutines"
+# endif
+# endif // __TESTSUITE_CORO_H_n4835
+
+#endif // __has_include(<experimental/coroutine>)
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+
+#include <cstdlib> /* for abort () */
+
+#ifndef OUTPUT
+# define PRINT(X)
+# define PRINTF (void)
+#else
+# define PRINT(X) puts(X)
+# define PRINTF printf
+#endif
--- /dev/null
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ // Some awaitables to use in tests.
+ // With progress printing for debug.
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ ~suspend_always_prt() { PRINT ("susp-always-dtor"); }
+ };
+
+ struct suspend_always_intprt {
+ int x;
+ suspend_always_intprt() : x(5) {}
+ suspend_always_intprt(int __x) : x(__x) {}
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-intprt");}
+ int await_resume() const noexcept { PRINT ("susp-always-resume-intprt"); return x;}
+ };
+
+ /* This returns the square of the int that it was constructed with. */
+ struct suspend_always_longprtsq {
+ long x;
+ suspend_always_longprtsq() : x(12L) { PRINT ("suspend_always_longprtsq def ctor"); }
+ suspend_always_longprtsq(long _x) : x(_x) { PRINTF ("suspend_always_longprtsq ctor with %ld\n", x); }
+ ~suspend_always_longprtsq() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-longsq");}
+ long await_resume() const noexcept { PRINT ("susp-always-resume-longsq"); return x * x;}
+ };
+
+ struct suspend_always_intrefprt {
+ int& x;
+ suspend_always_intrefprt(int& __x) : x(__x) {}
+ ~suspend_always_intrefprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-intprt");}
+ int& await_resume() const noexcept { PRINT ("susp-always-resume-intprt"); return x;}
+ };
+
+ struct promise_type {
+
+ promise_type() : vv(-1) { PRINT ("Created Promise"); }
+ promise_type(int __x) : vv(__x) { PRINTF ("Created Promise with %d\n",__x); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+
+#ifdef USE_AWAIT_TRANSFORM
+
+ auto await_transform (int v) {
+ PRINTF ("await_transform an int () %d\n",v);
+ return suspend_always_intprt (v);
+ }
+
+ auto await_transform (long v) {
+ PRINTF ("await_transform a long () %ld\n",v);
+ return suspend_always_longprtsq (v);
+ }
+
+#endif
+
+ auto yield_value (int v) {
+ PRINTF ("yield_value (%d)\n", v);
+ vv = v;
+ return suspend_always_prt{};
+ }
+
+#ifdef RETURN_VOID
+
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+
+#else
+
+ void return_value (int v) {
+ PRINTF ("return_value (%d)\n", v);
+ vv = v;
+ }
+
+#endif
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+
+ int get_value () { return vv; }
+ private:
+ int vv;
+ };
+
+};
--- /dev/null
+# Copyright (C) 2018-2020 Free Software Foundation, Inc.
+
+# Contributed by Iain Sandoe <iain@sandoe.co.uk> under contract to Facebook.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Test C++ coroutines, requires c++17; doesn't, at present, seem much
+# point in repeating these for other versions.
+
+# Load support procs.
+load_lib g++-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CXXFLAGS
+if ![info exists DEFAULT_CXXFLAGS] then {
+ set DEFAULT_CXXFLAGS " -pedantic-errors -Wno-long-long"
+}
+
+set DEFAULT_COROFLAGS $DEFAULT_CXXFLAGS
+lappend DEFAULT_COROFLAGS "-std=c++17" "-fcoroutines"
+
+dg-init
+
+# Run the tests.
+# g++-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] \
+# "" $DEFAULT_COROFLAGS
+
+foreach test [lsort [find $srcdir/$subdir {*.[CH]}]] {
+ if [runtest_file_p $runtests $test] {
+ set nshort [file tail [file dirname $test]]/[file tail $test]
+ verbose "Testing $nshort $DEFAULT_COROFLAGS" 1
+ dg-test $test "" $DEFAULT_COROFLAGS
+ set testcase [string range $test [string length "$srcdir/"] end]
+ }
+}
+
+# done.
+dg-finish
--- /dev/null
+// { dg-do run }
+
+// check the code-gen for the failed alloc return.
+
+#include "../coro.h"
+
+#if __has_include(<new>)
+# include <new>
+#else
+
+// Required when get_return_object_on_allocation_failure() is defined by
+// the promise.
+// we need a no-throw new, and new etc. build the relevant pieces here to
+// avoid needing the headers in the test.
+
+namespace std {
+ struct nothrow_t {};
+ constexpr nothrow_t nothrow = {};
+ typedef __SIZE_TYPE__ size_t;
+} // end namespace std
+
+void* operator new(std::size_t, const std::nothrow_t&) noexcept;
+void operator delete(void* __p, const std::nothrow_t&) noexcept;
+#endif
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () noexcept : handle(0) {}
+ coro1 (handle_type _handle) noexcept
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) noexcept : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) noexcept {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() noexcept {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ static coro1 get_return_object_on_allocation_failure () noexcept;
+ }; // promise
+}; // coro1
+
+coro1 coro1::promise_type::
+get_return_object_on_allocation_failure () noexcept {
+ PRINT ("alloc fail return");
+ return coro1 (nullptr);
+}
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// check codegen for overloaded operator new/delete.
+
+#include "../coro.h"
+
+int used_ovl_new = 0;
+int used_ovl_del = 0;
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () noexcept : handle(0) {}
+ coro1 (handle_type _handle) noexcept
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) noexcept : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) noexcept {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() noexcept {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ void *operator new (std::size_t sz) {
+ PRINT ("promise_type: used overloaded operator new");
+ used_ovl_new++;
+ return ::operator new(sz);
+ }
+
+ void operator delete (void *p) {
+ PRINT ("promise_type: used overloaded operator delete");
+ used_ovl_del++;
+ return ::operator delete(p);
+ }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ }; // promise
+}; // coro1
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ // Nest a scope so that we can inspect the flags after the DTORs run.
+ {
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ }
+ if (used_ovl_new != 1)
+ {
+ PRINT ("main: failed to call overloaded operator new");
+ abort ();
+ }
+ if (used_ovl_del != 1)
+ {
+ PRINT ("main: failed to call overloaded operator delete");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check that we can use co_await as a call parm.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+
+__attribute__((__noinline__))
+static int
+foo (int x)
+{
+ return x + 2;
+}
+
+/* Function with a single await. */
+coro1
+f ()
+{
+ gX = foo (co_await 9);
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+
+ PRINT ("main: resuming [1] (initial suspend)");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [2] (await 9 parm)");
+ f_coro.handle.resume();
+
+ if (gX != 11)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
+ abort ();
+ }
+
+ /* we should now have returned with the co_return 11 + 31) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done'");
+ abort ();
+ }
+
+ int y = f_coro.handle.promise().get_value();
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ abort ();
+ }
+
+ puts ("main: done");
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+// { dg-do run }
+
+// Check that we can use multiple co_awaits as a call parm.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+
+__attribute__((__noinline__))
+static int
+bar (int x, int y)
+{
+ return x + y;
+}
+
+/* Function with a multiple awaits. */
+coro1
+g ()
+{
+ gX = bar (co_await 9, co_await 2);
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 g_coro = g ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (g_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+
+ PRINT ("main: resuming [1] (initial suspend)");
+ g_coro.handle.resume();
+
+ PRINT ("main: resuming [2] (parm 1)");
+ g_coro.handle.resume();
+ PRINT ("main: resuming [2] (parm 2)");
+ g_coro.handle.resume();
+ if (gX != 11)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
+ abort ();
+ }
+
+ /* we should now have returned with the co_return 11 + 31) */
+ if (!g_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done'");
+ abort ();
+ }
+
+ int y = g_coro.handle.promise().get_value();
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ abort ();
+ }
+
+ puts ("main: done");
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+// { dg-do run }
+
+// Check foo (compiler temp, co_await).
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+
+__attribute__((__noinline__))
+static int
+bar (int x, int y)
+{
+ return x + y;
+}
+
+/* Function with a compiler temporary and a co_await. */
+coro1
+g ()
+{
+ gX = bar (gX + 8, co_await 2);
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 g_coro = g ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (g_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+
+ PRINT ("main: resuming [1] (initial suspend)");
+ g_coro.handle.resume();
+
+ PRINT ("main: resuming [2] (parm 1)");
+ g_coro.handle.resume();
+
+ if (gX != 11)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
+ abort ();
+ }
+
+ /* we should now have returned with the co_return 11 + 31) */
+ if (!g_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done'");
+ abort ();
+ }
+
+ int y = g_coro.handle.promise().get_value();
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ abort ();
+ }
+
+ puts ("main: done");
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+// { dg-do run }
+
+// Check foo (compiler temp, co_await).
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+
+__attribute__((__noinline__))
+static int
+bar (int x, const int& y)
+{
+ return x + y;
+}
+
+/* Function with a compiler temporary and a co_await. */
+coro1
+g ()
+{
+ gX = bar (gX + 8, co_await 2);
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 g_coro = g ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (g_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+
+ PRINT ("main: resuming [1] (initial suspend)");
+ g_coro.handle.resume();
+
+ PRINT ("main: resuming [2] (parm 1)");
+ g_coro.handle.resume();
+
+ if (gX != 11)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
+ abort ();
+ }
+
+ /* we should now have returned with the co_return 11 + 31) */
+ if (!g_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done'");
+ abort ();
+ }
+
+ int y = g_coro.handle.promise().get_value();
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ abort ();
+ }
+
+ puts ("main: done");
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+// { dg-do run }
+
+// Simplest class.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+class foo
+{
+ public:
+ coro1 meth ()
+ {
+ PRINT ("coro1: about to return");
+ co_return 42;
+ }
+};
+
+int main ()
+{
+ foo inst;
+
+ PRINT ("main: create coro1");
+ coro1 x = inst.meth ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Class with parm capture
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+class foo
+{
+ public:
+ coro1 meth (int x)
+ {
+ if (x > 30)
+ {
+ PRINT ("coro1: about to return k");
+ co_return 6174;
+ }
+ else if (x > 20)
+ {
+ PRINT ("coro1: about to return the answer");
+ co_return 42;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+ }
+};
+
+int main ()
+{
+ foo inst;
+
+ PRINT ("main: create coro1");
+ coro1 x = inst.meth (25);
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ {
+ PRINTF ("main: wrong result (%d)", y);
+ abort ();
+ }
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// template parm in a class
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+template <typename T>
+class foo
+{
+ public:
+ coro1 meth (T y)
+ {
+ PRINT ("coro1: about to return");
+ T x = y;
+ co_return co_await x + 3;
+ }
+};
+
+int main ()
+{
+ foo<int> inst {};
+ PRINT ("main: create coro1");
+ coro1 x = inst.meth (17);
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume (initial suspend)");
+
+ x.handle.resume();
+ PRINT ("main: after resume (co_await)");
+
+ /* Now we should have the co_returned value. */
+ int y = x.handle.promise().get_value();
+ if ( y != 20 )
+ {
+ PRINTF ("main: wrong result (%d).", y);
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// template parm in a class
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+template <typename T>
+class foo
+{
+ public:
+ coro1 operator()(T y)
+ {
+ PRINT ("coro1: about to return");
+ T x = y;
+ co_return co_await x + 3;
+ }
+};
+
+int main ()
+{
+ foo<int> inst {};
+ PRINT ("main: create coro1");
+ coro1 x = inst.operator()(17);
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume (initial suspend)");
+
+ x.handle.resume();
+ PRINT ("main: after resume (co_await)");
+
+ /* Now we should have the co_returned value. */
+ int y = x.handle.promise().get_value();
+ if ( y != 20 )
+ {
+ PRINTF ("main: wrong result (%d).", y);
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// template parm in a class
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+template <typename T>
+class foo
+{
+ public:
+ auto get_lam ()
+ {
+ auto l = [](T y) -> coro1
+ {
+ T x = y;
+ co_return co_await x + 3;
+ };
+ return l;
+ }
+};
+
+int main ()
+{
+ foo<int> inst {};
+ auto ll = inst.get_lam ();
+
+ PRINT ("main: create coro1");
+ int arg = 17; // avoid a dangling reference
+ coro1 x = ll (arg);
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume (initial suspend)");
+
+ x.handle.resume();
+ PRINT ("main: after resume (co_await)");
+
+ /* Now we should have the co_returned value. */
+ int y = x.handle.promise().get_value();
+ if ( y != 20 )
+ {
+ PRINTF ("main: wrong result (%d).", y);
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// template parm in a class
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+template <typename T>
+class foo
+{
+ public:
+ auto get_lam (int parm)
+ {
+ int local = 3;
+ auto l = [=](T y) -> coro1
+ {
+ T x = y;
+ co_return co_await x + local;
+ };
+ return l;
+ }
+};
+
+int main ()
+{
+ foo<int> inst {};
+ auto ll = inst.get_lam (10);
+
+ PRINT ("main: create coro1");
+ int arg = 17; // avoid a dangling reference
+ coro1 x = ll (arg);
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume (initial suspend)");
+
+ x.handle.resume();
+ PRINT ("main: after resume (co_await)");
+
+ /* Now we should have the co_returned value. */
+ int y = x.handle.promise().get_value();
+ if ( y != 20 )
+ {
+ PRINTF ("main: wrong result (%d).", y);
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// template parm in a class
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+template <typename T>
+class foo
+{
+ public:
+ void use_lambda ()
+ {
+ int a_copy = 20;
+ int a_ref = 10;
+
+ auto f = [&, a_copy]() -> coro1
+ {
+ co_yield a_ref + a_copy;
+ co_return a_ref + a_copy;
+ };
+
+ coro1 A = f ();
+ A.handle.resume(); // Initial suspend.
+ PRINT ("main: [a_copy = 20, a_ref = 10]");
+
+ int y = A.handle.promise().get_value();
+ if (y != 30)
+ {
+ PRINTF ("main: co-yield = %d, should be 30\n", y);
+ abort ();
+ }
+
+ a_copy = 5;
+ a_ref = 7;
+
+ A.handle.resume(); // from the yield.
+ PRINT ("main: [a_copy = 5, a_ref = 7]");
+
+ y = A.handle.promise().get_value();
+ if (y != 27)
+ {
+ PRINTF ("main: co-ret = %d, should be 27\n", y);
+ abort ();
+ }
+ PRINT ("use_lambda: about to return");
+ }
+ ~foo () { PRINT ("foo: DTOR"); }
+};
+
+int main ()
+{
+ foo<int> inst;
+ inst.use_lambda();
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// The simplest co_await we can do.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+
+coro1
+f ()
+{
+ co_await coro1::suspend_always_prt{};
+ co_return gX + 10;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1] initial suspend");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [2] co_await");
+ f_coro.handle.resume();
+ /* we should now have returned with the co_return (15) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 11)
+ {
+ PRINTF ("main: y is wrong : %d, should be 11\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+/* The simplest valued co_await we can do. */
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+
+coro1
+f ()
+{
+ gX = co_await coro1::suspend_always_intprt{};
+ co_return gX + 10;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1] initial suspend");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [2] co_await suspend_always_intprt");
+ f_coro.handle.resume();
+ if (gX != 5)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 5\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (15) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 15)
+ {
+ PRINTF ("main: y is wrong : %d, should be 15\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test of basic await transform, no local state.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+
+coro1
+f ()
+{
+ gX = co_await 11;
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1] initial suspend");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [2] co_await");
+ f_coro.handle.resume();
+ if (gX != 11)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 11\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (15) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Basic check of co_await with an expression to await transform.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+
+coro1
+f ()
+{
+ gX = co_await 11 + 15;
+ co_return gX + 16;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1] initial suspend");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [1] await");
+ f_coro.handle.resume();
+ if (gX != 26)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 26\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (26+16) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check correct operation of await transform.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+/* Valued with an await_transform. */
+int gX = 1;
+int y = 30;
+
+coro1
+f ()
+{
+ if (gX < 12) {
+ gX += y;
+ gX += co_await 11;
+ } else
+ gX += co_await 12;
+
+ co_return gX;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ PRINT ("main: gX OK -- looping");
+ do {
+ PRINTF ("main: gX : %d \n", gX);
+ f_coro.handle.resume();
+ } while (!f_coro.handle.done());
+ int y = f_coro.handle.promise().get_value();
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check correct operation of co_await in a loop without local state.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+/* Valued with an await_transform. */
+int gX = 1;
+
+coro1
+f ()
+{
+ for (;;)
+ {
+ gX += co_await 11;
+ if (gX > 100)
+ break;
+ }
+ co_return gX;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ PRINT ("main: gX OK -- looping");
+ do {
+ PRINTF ("main: gX : %d \n", gX);
+ f_coro.handle.resume();
+ } while (!f_coro.handle.done());
+
+ int y = f_coro.handle.promise().get_value();
+ // first value above 100 is 10*11 + 1.
+ if (y != 111)
+ {
+ PRINTF ("main: y is wrong : %d, should be 111\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Basic check of the co_await operator overload.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+/* A very simple overload. */
+struct empty
+{
+ auto operator co_await() const & noexcept {
+ return coro1::suspend_always_intprt{};
+ }
+};
+
+int gX = 1;
+empty e{};
+
+coro1
+f ()
+{
+ int a = co_await(e); /* operator ovl. */
+ co_return gX + 5 + a;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done'");
+ abort ();
+ }
+
+ PRINT ("main: resuming [1] initial suspend");
+ f_coro.handle.resume();
+
+ PRINT ("main: resuming [2] co_await");
+ f_coro.handle.resume();
+
+ /* we should now have returned with the co_return (11) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+
+ int y = f_coro.handle.promise().get_value();
+ if (y != 11)
+ {
+ PRINTF ("main: y is wrong : %d, should be 11\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check that we correctly operate when the coroutine object is templated.
+
+#include "../coro.h"
+
+template <typename T>
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT ("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT ("Moved coro1");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ return *this;
+ }
+ ~coro1() {
+ PRINT ("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ ~suspend_never_prt() {}
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type h) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ };
+
+ struct suspend_always_prt {
+ T x;
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept {PRINT ("susp-always-resume");}
+ };
+
+ /* This returns the int it was constructed with. */
+ struct suspend_always_intprt {
+ T x;
+ suspend_always_intprt() : x((T)5) { PRINT ("suspend_always_intprt def ctor"); }
+ suspend_always_intprt(T _x) : x(_x)
+ { PRINTF ("suspend_always_intprt ctor with %ld\n", (long)x); }
+ ~suspend_always_intprt() {}
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept { PRINT ("susp-always-susp-int");}
+ int await_resume() const noexcept { PRINT ("susp-always-resume-int"); return x;}
+ };
+
+ struct promise_type {
+ T value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object() {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+
+ auto initial_suspend() {
+ PRINT ("get initial_suspend ");
+ return suspend_never_prt{};
+ }
+
+ auto final_suspend() {
+ PRINT ("get final_suspend");
+ return suspend_always_prt{};
+ }
+
+ void return_value (int v) {
+ PRINTF ("return_value () %ld\n", (long) v);
+ value = v;
+ }
+
+ auto await_transform (T v) {
+ PRINTF ("await_transform a T () %ld\n", (long)v);
+ return suspend_always_intprt (v);
+ }
+
+ T get_value () { return value; }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+};
+
+/* Valued with an await_transform. */
+int gX = 2;
+
+template <typename T>
+coro1<T> f ()
+{
+ for (int i = 0; i < 4; ++i)
+ {
+ gX += co_await 10;
+ }
+ co_return gX;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ auto f_coro = f<int>();
+
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 2)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 2\n", gX);
+ abort ();
+ }
+ PRINT ("main: gX OK -- looping");
+ do {
+ f_coro.handle.resume();
+ } while (!f_coro.handle.done());
+
+ int y = f_coro.handle.promise().get_value();
+
+ if (y != 42)
+ {
+ PRINTF ("main: y is wrong : %d, should be 42\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check cascaded co_await operations.
+
+#include "../coro.h"
+
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+/* Valued with an await_transform. */
+int gX = 1;
+coro1 f ()
+{
+ /* We are going to use an await transform that takes a long, the
+ await_resume squares it.
+ so we get 11 ** 4, 14641. */
+ gX = (int) co_await co_await 11L;
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1] - inital suspend");
+ f_coro.handle.resume();
+
+ PRINT ("main: resuming [2] - nested");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [3] - outer");
+ f_coro.handle.resume();
+
+ if (gX != 14641)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 14641\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (14672) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 14672)
+ {
+ PRINTF ("main: y is wrong : %d, should be 14672\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+/* Valued with an await_transform. */
+int gX = 1;
+coro1 f ()
+{
+ gX = co_await 11 + co_await 15;
+ co_return gX + 31;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1] (initial suspend)");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [2] one side of add");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [3] other side of add");
+ f_coro.handle.resume();
+ if (gX != 26)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 26\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (57) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 57)
+ {
+ PRINTF ("main: y is wrong : %d, should be 57\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check type dependent function parms.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+// there is a promise ctor that takes a single int.
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+template <typename T>
+coro1
+f (T y) noexcept
+{
+ PRINT ("coro1: about to return");
+ T x = y;
+ co_return co_await x + 3;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f<int>(17);
+
+ /* We should have created the promise with an initial value of
+ 17. */
+ int y = x.handle.promise().get_value();
+ if ( y != 17 )
+ {
+ PRINTF ("main: wrong promise init (%d).", y);
+ abort ();
+ }
+
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume (initial suspend)");
+
+ x.handle.resume();
+ PRINT ("main: after resume (co_await)");
+
+ /* Now we should have the co_returned value. */
+ y = x.handle.promise().get_value();
+ if ( y != 20 )
+ {
+ PRINTF ("main: wrong result (%d).", y);
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test of forwarding a templated awaitable to co_await.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+/* Valued with an await_transform. */
+
+template< typename AWAITABLE >
+coro1
+test_fwd (AWAITABLE&& awaitable)
+{
+ // the await_resume() just returns the saved int value.
+ int a = co_await std::forward<AWAITABLE>(awaitable);
+ // Which we co-return to the promise so that it can be
+ // retrieved.
+ co_return a;
+}
+
+int main ()
+{
+ // We have an awaitable that stores the int it was constructed with.
+ coro1::suspend_always_intprt g(15);
+ struct coro1 g_coro = test_fwd (g);
+
+ PRINT ("main: resuming g [1] (initial suspend)");
+ g_coro.handle.resume();
+
+ PRINT ("main: resuming g [2] co_await");
+ g_coro.handle.resume();
+
+ int y = g_coro.handle.promise().get_value();
+ if (y != 15)
+ {
+ PRINTF ("main: y is wrong : %d, should be 15\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Basic check of the co_await operator overload.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+/* A very simple overload. */
+struct empty
+{
+ auto operator co_await() & noexcept {
+ return coro1::suspend_always_intprt{};
+ }
+ auto operator co_await() && noexcept {
+ return coro1::suspend_always_longprtsq(3L);
+ }
+};
+
+empty e{};
+
+coro1
+f ()
+{
+ int a = co_await e; /* operator ovl lv. */
+ int b = co_await empty{}; /* operator ovl rv. */
+ co_return b + a;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done'");
+ abort ();
+ }
+
+ PRINT ("main: resuming [1] initial suspend");
+ f_coro.handle.resume();
+
+ PRINT ("main: resuming [2] co_await a");
+ f_coro.handle.resume();
+
+ PRINT ("main: resuming [3] co_await b");
+ f_coro.handle.resume();
+
+ /* we should now have returned with the co_return (14) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+
+ int y = f_coro.handle.promise().get_value();
+ if (y != 14)
+ {
+ PRINTF ("main: y is wrong : %d, should be 14\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+/* The simplest valued co_await we can do. */
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+
+coro1
+f ()
+{
+ int t = 5;
+ gX = co_await coro1::suspend_always_intrefprt{t};
+ co_return t + 10;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - checking gX");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: we should not be 'done' [1]");
+ abort ();
+ }
+ PRINT ("main: resuming [1] initial suspend");
+ f_coro.handle.resume();
+ PRINT ("main: resuming [2] co_await suspend_always_intprt");
+ f_coro.handle.resume();
+ if (gX != 5)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 5\n", gX);
+ abort ();
+ }
+ /* we should now have returned with the co_return (15) */
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: we should be 'done' ");
+ abort ();
+ }
+ int y = f_coro.handle.promise().get_value();
+ if (y != 15)
+ {
+ PRINTF ("main: y is wrong : %d, should be 15\n", y);
+ abort ();
+ }
+ puts ("main: done");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Basic functionality check, co_return.
+// Here we check the case that initial suspend is "never", so that the co-
+// routine runs to completion immediately.
+
+#include "../coro.h"
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object () {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (never) ");
+ return suspend_never_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always) ");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - should be done");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently was not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Basic functionality check, co_return.
+// Here we check the case that initial suspend is "always".
+
+#include "../coro.h"
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object () {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// GRO differs from the eventual return type.
+
+# include "../coro.h"
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// GRO differs from eventual return type and has non-trivial dtor.
+
+#include "../coro.h"
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+
+ struct nontriv {
+ handle_type handle;
+ nontriv () : handle(0) {PRINT("nontriv nul ctor");}
+ nontriv (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created nontriv object from handle");
+ }
+ ~nontriv () {
+ PRINT("Destroyed nontriv");
+ }
+ };
+
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (nontriv _nt)
+ : handle(_nt.handle) {
+ PRINT("Created coro1 object from nontriv");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return nontriv(handle_type::from_promise (*this));
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return 42;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test returning a T.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+#include "../coro.h"
+
+struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept
+ { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+};
+
+/* NOTE: this has a DTOR to test that pathway. */
+struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept
+ { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+};
+
+template <typename T>
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct promise_type {
+ T value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+
+ auto initial_suspend () const {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () const {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (T v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ T get_value (void) { return value; }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+};
+
+coro1<float>
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return (float) 42;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ coro1<float> x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != (float)42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check that "co_return (void)expression;" evaluates expression once.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define RETURN_VOID
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+__attribute__((__noinline__))
+int foo (void) { PRINT ("called the int fn foo"); gX +=1 ; return gX; }
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return (void)foo();
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ // We want to check that foo() was called exactly once.
+ if (gX != 2)
+ {
+ PRINT ("main: failed check for a single call to foo()");
+ abort ();
+ }
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test templated co-return.
+
+#include "../coro.h"
+
+struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept
+ { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+};
+
+/* NOTE: this has a DTOR to test that pathway. */
+struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coro::coroutine_handle<>) const noexcept
+ { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+};
+
+template <typename T>
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct promise_type {
+ T value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ suspend_always_prt initial_suspend () const {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ suspend_always_prt final_suspend () const {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (T v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ T get_value (void) { return value; }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+};
+
+template <typename T>
+coro1<T> f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return (T)42;
+}
+
+// The test will only really for int, but that's OK here.
+int main ()
+{
+ PRINT ("main: create coro1");
+ auto x = f<int>();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// test boolean return from await_suspend ().
+
+#include "../coro.h"
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ bool await_suspend(handle_type) const noexcept {
+ PRINT ("susp-never-susp"); // never executed.
+ return true; // ...
+ }
+ void await_resume() const noexcept {PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ bool await_suspend(handle_type) const noexcept {
+ PRINT ("susp-always-susp, but we're going to continue.. ");
+ return false; // not going to suspend.
+ }
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ coro1 get_return_object () {
+ PRINT ("get_return_object: from handle from promise");
+ return coro1 (handle_type::from_promise (*this));
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always, but really never) ");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always, but never) ");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ auto p = x.handle.promise ();
+ auto aw = p.initial_suspend();
+ auto f = aw.await_suspend(coro::coroutine_handle<coro1::promise_type>::from_address ((void *)&x));
+ PRINT ("main: got coro1 - should be done");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently was not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check that "co_return expression;" only evaluates expression once.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+/* Give foo() a measureable side-effect. */
+int gX = 1;
+__attribute__((__noinline__))
+int foo (void)
+{
+ PRINT ("called the int fn foo");
+ gX += 1;
+ return gX;
+}
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return foo();
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ // We want to check that foo() was called exactly once.
+ if (gX != 2)
+ {
+ PRINT ("main: failed check for a single call to foo()");
+ abort ();
+ }
+ PRINT ("main: after resume");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check co_return co_await
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return co_await coro1::suspend_always_intprt{};
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume 1 (initial suspend)");
+ x.handle.resume();
+ PRINT ("main: after resume 2 (await intprt)");
+
+ int y = x.handle.promise().get_value();
+ if ( y != 5 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check co_return function (co_await)
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+__attribute__((__noinline__))
+static int
+foo (int x)
+{
+ return x + 2;
+}
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return foo (co_await 5);
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume 1 (initial suspend)");
+ x.handle.resume();
+ PRINT ("main: after resume 2 (await parm)");
+
+ int y = x.handle.promise().get_value();
+ if ( y != 7 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check type dependent function parms.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+// there is a promise ctor that takes a single int.
+#include "../coro1-ret-int-yield-int.h"
+
+template <typename T>
+coro1
+f (T y) noexcept
+{
+ PRINT ("coro1: about to return");
+ T x = y;
+ co_return 3;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f<int>(17);
+
+ /* We should have created the promise with an initial value of
+ 17. */
+ int y = x.handle.promise().get_value();
+ if ( y != 17 )
+ {
+ PRINT ("main: wrong promise init.");
+ abort ();
+ }
+
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume (initial suspend)");
+
+ /* Now we should have the co_returned value. */
+ y = x.handle.promise().get_value();
+ if ( y != 3 )
+ {
+ PRINT ("main: wrong answer.");
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check type dependent function parms.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+// there is a promise ctor that takes a single int.
+#include "../coro1-ret-int-yield-int.h"
+
+template <typename T, typename U, typename V>
+coro1
+f (T x, U y, V z) noexcept
+{
+ PRINT ("coro1: about to return");
+ T xi = (T) y;
+ T yi = (T) z;
+ T zi = x;
+ co_return 3 + xi + yi + zi;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f<int, float, double>(2, 18.0F, 19.0);
+
+ /* We should be using the default promise ctor, which sets the value
+ to -1. */
+ int y = x.handle.promise().get_value();
+ if ( y != -1 )
+ {
+ PRINT ("main: wrong promise init.");
+ abort ();
+ }
+
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume (initial suspend)");
+
+ /* Now we should have the co_returned value. */
+ y = x.handle.promise().get_value();
+ if ( y != 42 )
+ {
+ PRINT ("main: wrong answer.");
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test yielding an int.
+
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+// Check that we resolve the correct overload for the yield_value method.
+
+#include "../coro.h"
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ auto yield_value (int v) {
+ PRINTF ("yield_value () %d and suspend always\n",v);
+ value = v;
+ return suspend_always_prt{};
+ }
+ /* Some non-matching overloads. */
+ auto yield_value (suspend_always_prt s, int x) {
+ return s;
+ }
+ auto yield_value (void) {
+ return 42;
+ }
+ int get_value (void) { return value; }
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("f: about to yield 42");
+ co_yield 42;
+
+ PRINT ("f: about to return 6174");
+ co_return 6174;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming (1)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got 42");
+ PRINT ("main: got coro1 - resuming (2)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (2)");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test yielding an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f () noexcept
+{
+ PRINT ("f: about to yield 42");
+ co_yield 42;
+
+ PRINT ("f: about to yield 11");
+ co_yield 11;
+
+ PRINT ("f: about to return 6174");
+ co_return 6174;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming (1)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got 42 - resuming (2)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (2)");
+ y = x.handle.promise().get_value();
+ if ( y != 11 )
+ abort ();
+ PRINT ("main: apparently got 11 - resuming (3)");
+ if (x.handle.done())
+ {
+ PRINT ("main: done?");
+ abort();
+ }
+ x.handle.resume();
+ PRINT ("main: after resume (2) checking return");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test co_yield in a loop with no local state.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int gX = 1;
+
+struct coro1
+f () noexcept
+{
+ for (gX = 5; gX < 10 ; gX++)
+ {
+ PRINTF ("f: about to yield %d\n", gX);
+ co_yield gX;
+ }
+
+ PRINT ("f: about to return 6174");
+ co_return 6174;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 f_coro = f ();
+ PRINT ("main: got coro1 - resuming (1)");
+ if (gX != 1)
+ {
+ PRINTF ("main: gX is wrong : %d, should be 1\n", gX);
+ abort ();
+ }
+ if (f_coro.handle.done())
+ abort();
+ f_coro.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = f_coro.handle.promise().get_value();
+ if (y != 5)
+ {
+ PRINTF ("main: got %d not 5.\n",y);
+ abort ();
+ }
+ PRINT ("main: gX OK -- looping");
+ do {
+ y = f_coro.handle.promise().get_value();
+ if (y != gX)
+ {
+ PRINTF ("main: got %d not %d.\n",y, gX);
+ abort ();
+ }
+ PRINTF ("main: gX : %d \n", gX);
+ f_coro.handle.resume();
+ } while (!f_coro.handle.done());
+
+ y = f_coro.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test co_yield in templated code.
+
+#include "../coro.h"
+
+template <typename T>
+struct looper {
+
+ struct promise_type {
+ T value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+
+ void return_value (T v) {
+ PRINTF ("return_value () %lf\n", (double)v);
+ value = v;
+ }
+
+ auto yield_value (T v) {
+ PRINTF ("yield_value () %lf and suspend always\n", (double)v);
+ value = v;
+ return suspend_always_prt{};
+ }
+
+ T get_value (void) { return value; }
+
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+
+ using handle_type = coro::coroutine_handle<looper::promise_type>;
+ handle_type handle;
+
+ looper () : handle(0) {}
+ looper (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ looper (const looper &) = delete; // no copying
+ looper (looper &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("looper mv ctor ");
+ }
+ looper &operator = (looper &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("looper op= ");
+ return *this;
+ }
+ ~looper() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+};
+
+// Contrived to avoid non-scalar state across the yield.
+template <typename T>
+looper<T> f () noexcept
+{
+ for (int i = 5; i < 10 ; ++i)
+ {
+ PRINTF ("f: about to yield %d\n", i);
+ co_yield (T) i;
+ }
+
+ PRINT ("f: about to return 6174");
+ co_return 6174;
+}
+
+// contrived, only going to work for an int.
+int main ()
+{
+ PRINT ("main: create int looper");
+ auto f_coro = f<int> ();
+
+ if (f_coro.handle.done())
+ {
+ PRINT ("main: said we were done, but we hadn't started!");
+ abort();
+ }
+
+ PRINT ("main: OK -- looping");
+ int y, test = 5;
+ do {
+ f_coro.handle.resume();
+ if (f_coro.handle.done())
+ break;
+ y = f_coro.handle.promise().get_value();
+ if (y != test)
+ {
+ PRINTF ("main: failed for test %d, got %d\n", test, y);
+ abort();
+ }
+ test++;
+ } while (test < 20);
+
+ y = f_coro.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+
+ PRINT ("main: apparently got 6174");
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// using non-trivial types in the coro.
+
+# include "../coro.h"
+
+#include <vector>
+#include <string>
+
+template <typename T>
+struct looper {
+
+ struct promise_type {
+ T value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+
+ void return_value (T v) {
+ PRINTF ("return_value () %s\n", v.c_str());
+ value = v;
+ }
+
+ auto yield_value (T v) {
+ PRINTF ("yield_value () %s and suspend always\n", v.c_str());
+ value = v;
+ return suspend_always_prt{};
+ }
+
+ T get_value (void) { return value; }
+
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+
+ using handle_type = coro::coroutine_handle<looper::promise_type>;
+ handle_type handle;
+
+ looper () : handle(0) {}
+ looper (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ looper (const looper &) = delete; // no copying
+ looper (looper &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("looper mv ctor ");
+ }
+ looper &operator = (looper &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("looper op= ");
+ return *this;
+ }
+ ~looper() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+};
+
+int gX ;
+
+struct mycounter
+{
+ mycounter () : v(0) { PRINT ("mycounter CTOR"); }
+ ~mycounter () { gX = 6174; PRINT ("mycounter DTOR"); }
+ int value () { return v; }
+ void incr () { v++; }
+ int v;
+};
+
+template <typename T>
+looper<T> with_ctorable_state (std::vector<T> d) noexcept
+{
+ std::vector<T> loc;
+ unsigned lim = d.size()-1;
+ mycounter c;
+ for (unsigned i = 0; i < lim ; ++i)
+ {
+ loc.push_back(d[i]);
+ c.incr();
+ PRINTF ("f: about to yield value %d \n", i);
+ co_yield loc[i];
+ }
+ loc.push_back(d[lim]);
+
+ PRINT ("f: done");
+ co_return loc[lim];
+}
+
+int main ()
+{
+ PRINT ("main: create looper");
+ std::vector<std::string> input = {"first", "the", "quick", "reddish", "fox", "done" };
+ auto f_coro = with_ctorable_state<std::string> (input);
+
+ PRINT ("main: got looper - resuming (1)");
+ if (f_coro.handle.done())
+ abort();
+
+ f_coro.handle.resume();
+ std::string s = f_coro.handle.promise().get_value();
+ if ( s != "first" )
+ abort ();
+
+ PRINTF ("main: got : %s\n", s.c_str());
+ unsigned check = 1;
+ do {
+ f_coro.handle.resume();
+ s = f_coro.handle.promise().get_value();
+ if (s != input[check++])
+ abort ();
+ PRINTF ("main: got : %s\n", s.c_str());
+ } while (!f_coro.handle.done());
+
+ if ( s != "done" )
+ abort ();
+
+ PRINT ("main: should be done");
+ if (!f_coro.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+
+ if (gX != 6174)
+ {
+ PRINT ("main: apparently we didn't run mycounter DTOR...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check co_return co_await
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f () noexcept
+{
+ PRINT ("f: about to yield");
+ co_yield co_await coro1::suspend_always_intprt(42);
+
+ PRINT ("f: about to return 6174");
+ co_return 6174;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ if (x.handle.done())
+ abort();
+
+ PRINT ("main: resuming (initial suspend)");
+ x.handle.resume();
+ PRINT ("main: resuming (await intprt)");
+ x.handle.resume();
+
+ PRINT ("main: after resume (2)");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got 42");
+
+ PRINT ("main: got coro1 - resuming (co_yield)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+
+ PRINT ("main: after resume (co_yield)");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check co_return co_await
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+__attribute__((__noinline__))
+static int
+foo (int x)
+{
+ return x + 2;
+}
+
+/* Function with a single await. */
+struct coro1
+f () noexcept
+{
+ PRINT ("f: about to yield");
+ co_yield foo (co_await 40);
+
+ PRINT ("f: about to return 6174");
+ co_return 6174;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ if (x.handle.done())
+ abort();
+
+ PRINT ("main: resuming (initial suspend)");
+ x.handle.resume();
+ PRINT ("main: resuming (await intprt)");
+ x.handle.resume();
+
+ PRINT ("main: after resume (2)");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got 42");
+
+ PRINT ("main: got coro1 - resuming (co_yield)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+
+ PRINT ("main: after resume (co_yield)");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check type dependent function parms.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+// there is a promise ctor that takes a single int.
+
+#include "../coro1-ret-int-yield-int.h"
+
+template <typename T>
+coro1
+f (T y) noexcept
+{
+ PRINT ("coro1: about to return");
+ T x = y;
+ co_yield x + 3;
+ co_return 42;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f<int>(17);
+
+ /* We should have created the promise with an initial value of
+ 17. */
+ int y = x.handle.promise().get_value();
+ if ( y != 17 )
+ {
+ PRINTF ("main: wrong promise init (%d).", y);
+ abort ();
+ }
+ if (x.handle.done())
+ abort();
+
+ PRINT ("main: got coro1 - resuming");
+ x.handle.resume();
+ PRINT ("main: after resume (initial suspend)");
+
+ if (x.handle.done())
+ abort();
+
+ /* Now we should have the co_yielded value. */
+ y = x.handle.promise().get_value();
+ if ( y != 20 )
+ {
+ PRINTF ("main: wrong result (%d).", y);
+ abort ();
+ }
+
+ PRINT ("main: after resume (co_yield)");
+ x.handle.resume();
+
+ /* now we should have the co_returned value. */
+ y = x.handle.promise().get_value();
+ if ( y != 42 )
+ {
+ PRINTF ("main: wrong result (%d).", y);
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check co_return co_await
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+/* Tests for . */
+struct test
+{
+ auto operator co_await() & noexcept {
+ return coro1::suspend_always_intprt{};
+ }
+
+ auto operator co_await() && noexcept {
+ return coro1::suspend_always_longprtsq(3L);
+ }
+};
+
+struct coro1
+f (test thing) noexcept
+{
+ co_yield co_await static_cast<test&&>(thing);
+ co_return 6174;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+
+ struct coro1 x = f (test{});
+ if (x.handle.done())
+ abort();
+
+ PRINT ("main: resuming (initial suspend)");
+ x.handle.resume();
+ PRINT ("main: resuming (await intprt)");
+ x.handle.resume();
+
+ int y = x.handle.promise().get_value();
+ if ( y != 9 )
+ {
+ PRINTF ("main: co-yield gave %d, should be 9\n", y);
+ abort ();
+ }
+
+ PRINT ("main: got coro1 - resuming (co_yield)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ {
+ PRINTF ("main: co-return gave %d, should be 9\n", y);
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Check co_return co_await
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+/* A very simple overload. */
+struct test
+{
+ auto operator co_await() & noexcept {
+ return coro1::suspend_always_intprt{};
+ }
+
+ auto operator co_await() && noexcept {
+ return coro1::suspend_always_longprtsq(3L);
+ }
+};
+
+template<typename RESULT, typename PARAM>
+RESULT
+f (PARAM thing) noexcept
+{
+ co_yield co_await static_cast<PARAM&&>(thing);
+ co_return 6174;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f<coro1, test> (test{});
+ if (x.handle.done())
+ abort();
+
+ PRINT ("main: resuming (initial suspend)");
+ x.handle.resume();
+ PRINT ("main: resuming (await intprt)");
+ x.handle.resume();
+
+ int y = x.handle.promise().get_value();
+ if ( y != 9 )
+ {
+ PRINTF ("main: co-yield gave %d, should be 9\n", y);
+ abort ();
+ }
+
+ PRINT ("main: got coro1 - resuming (co_yield)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ {
+ PRINTF ("main: co-return gave %d, should be 9\n", y);
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+# This harness is for tests that should be run at all optimisation levels.
+
+load_lib g++-dg.exp
+load_lib torture-options.exp
+
+global DG_TORTURE_OPTIONS LTO_TORTURE_OPTIONS
+
+dg-init
+torture-init
+
+set DEFAULT_COROFLAGS $DEFAULT_CXXFLAGS
+lappend DEFAULT_COROFLAGS "-std=c++17" "-fcoroutines"
+
+set-torture-options [concat $DG_TORTURE_OPTIONS $LTO_TORTURE_OPTIONS]
+
+gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.C]] "" $DEFAULT_COROFLAGS
+
+torture-finish
+dg-finish
--- /dev/null
+// { dg-do run }
+
+// Test exceptions.
+
+#include "../coro.h"
+#include <exception>
+
+int gX = 0;
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ PRINT("Destroyed coro1");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ };
+
+ /* NOTE: this has a DTOR to test that pathway. */
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp"); }
+ void await_resume() const noexcept { PRINT ("susp-always-resume"); }
+ ~suspend_always_prt() { PRINT ("susp-always-DTOR"); }
+ };
+
+ struct promise_type {
+ int value;
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { PRINT ("Destroyed Promise"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_value (int v) {
+ PRINTF ("return_value () %d\n",v);
+ value = v;
+ }
+ auto yield_value (int v) {
+ PRINTF ("yield_value () %d and suspend always\n",v);
+ value = v;
+ return suspend_always_prt{};
+ }
+ /* Some non-matching overloads. */
+ auto yield_value (suspend_always_prt s, int x) {
+ return s;
+ }
+ auto yield_value (void) {
+ return 42;//suspend_always_prt{};
+ }
+ int get_value (void) { return value; }
+
+ void unhandled_exception() {
+ PRINT ("unhandled_exception: caught one!");
+ gX = -1;
+ // returning from here should end up in final_suspend.
+ }
+ };
+};
+
+// So we want to check that the internal behaviour of try/catch is
+// working OK - and that if we have an unhandled exception it is caught
+// by the wrapper that we add to the rewritten func.
+
+struct coro1 throw_and_catch () noexcept
+{
+ int caught = 0;
+
+ try {
+ PRINT ("f: about to yield 42");
+ co_yield 42;
+
+ throw (20);
+
+ PRINT ("f: about to yield 6174");
+ co_return 6174;
+
+ } catch (int x) {
+ PRINTF ("f: caught %d\n", x);
+ caught = x;
+ }
+
+ PRINTF ("f: about to yield what we caught %d\n", caught);
+ co_yield caught;
+
+ throw ("bah");
+
+ PRINT ("f: about to return 22");
+ co_return 22;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = throw_and_catch ();
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: got coro, resuming..");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got the expected 42");
+ if (x.handle.done())
+ abort();
+ PRINT ("main: resuming...");
+ x.handle.resume();
+
+ y = x.handle.promise().get_value();
+ if ( y != 20 )
+ abort ();
+ PRINT ("main: apparently got 20, which we expected");
+ if (x.handle.done())
+ abort();
+
+ PRINT ("main: resuming...");
+ x.handle.resume();
+ // This should cause the throw of "bah" which is unhandled.
+ // We should catch the unhandled exception and then fall through
+ // to the final suspend point... thus be "done".
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ // When we caught the unhandled exception we flagged it instead of
+ // std::terminate-ing.
+ if (gX != -1)
+ {
+ PRINT ("main: apparently failed to catch");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test promise construction from function args list.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f (int x) noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return 42;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (555);
+ int y = x.handle.promise().get_value();
+ if ( y != 555 )
+ {
+ PRINT ("main: incorrect ctor value");
+ abort ();
+ }
+ PRINTF ("main: after coro1 ctor %d - now resuming\n", y);
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Simplest test that we correctly handle function params in the body
+// of the coroutine. No local state, just the parm.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f (int x) noexcept
+{
+ if (x > 20)
+ {
+ PRINT ("coro1: about to return k");
+ co_return 6174;
+ }
+ else
+ {
+ PRINT ("coro1: about to return the answer");
+ co_return 42;
+ }
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (32);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test that we correctly re-write multiple uses of a function param
+// in the body.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f (int x) noexcept
+{
+ if (x > 30)
+ {
+ PRINT ("coro1: about to return k");
+ co_return 6174;
+ }
+ else if (x > 20)
+ {
+ PRINT ("coro1: about to return the answer");
+ co_return 42;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (25);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test that we can use a function param in a co_xxxx status.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f (int x) noexcept
+{
+ if (x > 30)
+ {
+ PRINT ("coro1: about to return k");
+ co_return 6174;
+ }
+ else if (x > 20)
+ {
+ PRINTF ("coro1: about to co-return %d", x);
+ co_return x;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (25);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 25 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test that we can manage a constructed param copy.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+// Require a ctor.
+struct nontriv {
+ int a, b, c;
+ nontriv (int _a, int _b, int _c) : a(_a), b(_b), c(_c) {}
+ virtual int getA () { return a; }
+};
+
+struct coro1
+f (nontriv t) noexcept
+{
+ if (t.a > 30)
+ {
+ PRINTF ("coro1: about to return %d", t.b);
+ co_return t.b;
+ }
+ else if (t.a > 20)
+ {
+ PRINTF ("coro1: about to co-return %d", t.c);
+ co_return t.c;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ nontriv test (25, 6174, 42);
+ struct coro1 x = f (test);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test that we can manage a constructed param reference
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+// Require a ctor.
+struct nontriv {
+ int a, b, c;
+ nontriv (int _a, int _b, int _c) : a(_a), b(_b), c(_c) {}
+ virtual int getA () { return a; }
+};
+
+struct coro1
+f (nontriv &t) noexcept
+{
+ if (t.a > 30)
+ {
+ PRINTF ("coro1: about to return %d", t.b);
+ co_return t.b;
+ }
+ else if (t.a > 20)
+ {
+ PRINTF ("coro1: about to co-return %d", t.c);
+ co_return t.c;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ nontriv test (25, 6174, 42);
+ struct coro1 x = f (test);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// check references are handled as expected.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+coro1
+f (int& a_ref, int a_copy)
+{
+ co_yield a_ref + a_copy;
+ co_return a_ref + a_copy;
+}
+
+int main ()
+{
+ int a_copy = 20;
+ int a_ref = 10;
+
+ coro1 A = f (a_ref, a_copy);
+ A.handle.resume(); // Initial suspend.
+ PRINT ("main: [a_copy = 20, a_ref = 10]");
+
+ int y = A.handle.promise().get_value();
+ if (y != 30)
+ {
+ PRINTF ("main: co-yield = %d, should be 30\n", y);
+ abort ();
+ }
+
+ a_copy = 5;
+ a_ref = 7;
+
+ A.handle.resume();
+ PRINT ("main: [a_copy = 5, a_ref = 7]");
+
+ y = A.handle.promise().get_value();
+ if (y != 27)
+ {
+ PRINTF ("main: co-ret = %d, should be 27\n", y);
+ abort ();
+ }
+
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Simplest lambda
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ auto f = []() -> coro1
+ {
+ PRINT ("coro1: about to return");
+ co_return 42;
+ };
+
+ PRINT ("main: create coro1");
+ coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Lambda with parm
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ auto f = [](int x) -> coro1
+ {
+ if (x > 30)
+ {
+ PRINT ("coro1: about to return k");
+ co_return 6174;
+ }
+ else if (x > 20)
+ {
+ PRINT ("coro1: about to return the answer");
+ co_return 42;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+ };
+
+ PRINT ("main: create coro1");
+ coro1 x = f (25);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// lambda with parm and local state
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ auto f = [](int start) -> coro1
+ {
+ int value = start;
+ PRINT ("f: about to yield start");
+ co_yield start;
+
+ value -= 31;
+ PRINT ("f: about to yield (value-31)");
+ co_yield value;
+
+ value += 6163;
+ PRINT ("f: about to return (value+6163)");
+ co_return value;
+ };
+
+ PRINT ("main: create coro1");
+ coro1 x = f (42);
+ PRINT ("main: got coro1 - resuming (1)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got 42 - resuming (2)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (2)");
+ y = x.handle.promise().get_value();
+ if ( y != 11 )
+ abort ();
+ PRINT ("main: apparently got 11 - resuming (3)");
+ if (x.handle.done())
+ {
+ PRINT ("main: done?");
+ abort();
+ }
+ x.handle.resume();
+ PRINT ("main: after resume (2) checking return");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// generic Lambda with auto parm (c++14)
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ auto f = [](auto y) -> coro1
+ {
+ PRINT ("coro1: about to return");
+ auto x = y;
+ co_return co_await x + 3;
+ };
+
+ PRINT ("main: create coro1");
+ struct coro1 x = f((int)17);
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume (initial suspend)");
+
+ x.handle.resume();
+ PRINT ("main: after resume (co_await)");
+
+ /* Now we should have the co_returned value. */
+ int y = x.handle.promise().get_value();
+ if ( y != 20 )
+ {
+ PRINTF ("main: wrong result (%d).", y);
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+// { dg-additional-options "-std=c++2a" }
+
+// generic Lambda with template parm (from c++20)
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#define USE_AWAIT_TRANSFORM
+#include "../coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ auto f = []<typename T>(T y) -> coro1
+ {
+ PRINT ("coro1: about to return");
+ T x = y;
+ co_return co_await x + 3;
+ };
+
+ PRINT ("main: create coro1");
+ coro1 x = f.operator()<int>(17);
+ if (x.handle.done())
+ abort();
+
+ x.handle.resume();
+ PRINT ("main: after resume (initial suspend)");
+
+ x.handle.resume();
+ PRINT ("main: after resume (co_await)");
+
+ /* Now we should have the co_returned value. */
+ int y = x.handle.promise().get_value();
+ if ( y != 20 )
+ {
+ PRINTF ("main: wrong result (%d).", y);
+ abort ();
+ }
+
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// lambda with parm and local state
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ int local = 31;
+
+ auto f = [=](int start) -> coro1
+ {
+ int value = start;
+ PRINT ("f: about to yield start");
+ co_yield start;
+
+ value -= local;
+ PRINT ("f: about to yield (value-31)");
+ co_yield value;
+
+ value += 6163;
+ PRINT ("f: about to return (value+6163)");
+ co_return value;
+ };
+
+ PRINT ("main: create coro1");
+ coro1 x = f (42);
+ PRINT ("main: got coro1 - resuming (1)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got 42 - resuming (2)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (2)");
+ y = x.handle.promise().get_value();
+ if ( y != 11 )
+ abort ();
+ PRINT ("main: apparently got 11 - resuming (3)");
+ if (x.handle.done())
+ {
+ PRINT ("main: done?");
+ abort();
+ }
+ x.handle.resume();
+ PRINT ("main: after resume (2) checking return");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// lambda with parm and local state
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ int a_copy = 20;
+ int a_ref = 10;
+
+ auto f = [&, a_copy]() -> coro1
+ {
+ co_return a_ref + a_copy;
+ };
+
+ {
+ coro1 A = f ();
+ A.handle.resume();
+ PRINT ("main: [a_copy = 20, a_ref = 10]");
+
+ int y = A.handle.promise().get_value();
+ if (y != 30)
+ {
+ PRINTF ("main: A co-ret = %d, should be 30\n", y);
+ abort ();
+ }
+ }
+
+ a_copy = 5;
+ a_ref = 7;
+
+ coro1 B = f ();
+ B.handle.resume();
+ PRINT ("main: [a_copy = 5, a_ref = 7]");
+
+ int y = B.handle.promise().get_value();
+ if (y != 27)
+ {
+ PRINTF ("main: B co-ret = %d, should be 27\n", y);
+ abort ();
+ }
+
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// lambda with parm and local state
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ int a_copy = 20;
+ int a_ref = 10;
+
+ auto f = [&, a_copy]() -> coro1
+ {
+ co_yield a_ref + a_copy;
+ co_return a_ref + a_copy;
+ };
+
+ coro1 A = f ();
+ A.handle.resume(); // Initial suspend.
+ PRINT ("main: [a_copy = 20, a_ref = 10]");
+
+ int y = A.handle.promise().get_value();
+ if (y != 30)
+ {
+ PRINTF ("main: co-yield = %d, should be 30\n", y);
+ abort ();
+ }
+
+ a_copy = 5;
+ a_ref = 7;
+
+ A.handle.resume();
+ PRINT ("main: [a_copy = 5, a_ref = 7]");
+
+ y = A.handle.promise().get_value();
+ if (y != 27)
+ {
+ PRINTF ("main: co-ret = %d, should be 27\n", y);
+ abort ();
+ }
+
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test that we can use a function param in a co_xxxx status.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+int main ()
+{
+ int val;
+
+ auto f = [&] (int x) -> coro1
+ {
+ if (val + x > 25)
+ {
+ PRINT ("coro1: about to return k");
+ co_return 6174;
+ }
+ else if (val + x > 20)
+ {
+ PRINTF ("coro1: about to co-return %d\n", val + x);
+ co_return val + x;
+ }
+ else if (val + x > 5)
+ {
+ PRINTF ("coro1: about to co-return %d\n", val);
+ co_return val;
+ }
+ else
+ {
+ PRINT ("coro1: about to return 0");
+ co_return 0;
+ }
+ };
+
+ PRINT ("main: create coro1");
+
+ val = 20; // We should get this by ref.
+ int arg = 5; // and this as a regular parm.
+
+ coro1 x = f (arg);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 25 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Simplest local decl.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f () noexcept
+{
+ const int answer = 42;
+ PRINTF ("coro1: about to return %d\n", answer);
+ co_return answer;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Simplest local var
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f (int x) noexcept
+{
+ int answer = x + 6132;
+ PRINTF ("coro1: about to return %d\n", answer);
+ co_return answer;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (42);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test local vars in nested scopes
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f (int x) noexcept
+{
+ int y = x;
+ const int test = 20;
+ if (y > test)
+ {
+ int fred = y - 20;
+ PRINTF ("coro1: about to return %d\n", fred);
+ co_return fred;
+ }
+ else
+ {
+ PRINT ("coro1: about to return the answer\n");
+ co_return y;
+ }
+
+ co_return x;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (6194);
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume");
+ int y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ //x.handle.resume();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test modifying a local var and yielding several instances of it.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f (int start) noexcept
+{
+ int value = start;
+ PRINT ("f: about to yield start");
+ co_yield start;
+
+ value -= 31;
+ PRINT ("f: about to yield (value-31)");
+ co_yield value;
+
+ value += 6163;
+ PRINT ("f: about to return (value+6163)");
+ co_return value;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (42);
+ PRINT ("main: got coro1 - resuming (1)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = x.handle.promise().get_value();
+ if ( y != 42 )
+ abort ();
+ PRINT ("main: apparently got 42 - resuming (2)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (2)");
+ y = x.handle.promise().get_value();
+ if ( y != 11 )
+ abort ();
+ PRINT ("main: apparently got 11 - resuming (3)");
+ if (x.handle.done())
+ {
+ PRINT ("main: done?");
+ abort();
+ }
+ x.handle.resume();
+ PRINT ("main: after resume (2) checking return");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+// Test modifying a local var across nested scopes containing vars
+// hiding those at outer scopes.
+
+#include "../coro.h"
+
+// boiler-plate for tests of codegen
+#include "../coro1-ret-int-yield-int.h"
+
+struct coro1
+f (int start) noexcept
+{
+ int value = start;
+ {
+ int value = start + 5;
+ {
+ int value = start + 20;
+ }
+ {
+ int value = start + 1;
+ PRINT ("f: about to yield start");
+ co_yield value;
+ }
+ }
+
+ value -= 31;
+ PRINT ("f: about to yield (value-31)");
+ co_yield value;
+
+ value += 6163;
+ PRINT ("f: about to return (value+6163)");
+ co_return value;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f (42);
+ PRINT ("main: got coro1 - resuming (1)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (1)");
+ int y = x.handle.promise().get_value();
+ if ( y != 43 )
+ abort ();
+ PRINT ("main: apparently got 42 - resuming (2)");
+ if (x.handle.done())
+ abort();
+ x.handle.resume();
+ PRINT ("main: after resume (2)");
+ y = x.handle.promise().get_value();
+ if ( y != 11 )
+ abort ();
+ PRINT ("main: apparently got 11 - resuming (3)");
+ if (x.handle.done())
+ {
+ PRINT ("main: done?");
+ abort();
+ }
+ x.handle.resume();
+ PRINT ("main: after resume (2) checking return");
+ y = x.handle.promise().get_value();
+ if ( y != 6174 )
+ abort ();
+ PRINT ("main: apparently got 6174");
+ if (!x.handle.done())
+ {
+ PRINT ("main: apparently not done...");
+ abort ();
+ }
+ PRINT ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+// { dg-output "main: returning\n" }
+// { dg-output "Destroyed coro1\n" }
+// { dg-output "Destroyed suspend_always_prt\n" }
+// { dg-output "Destroyed Promise\n" }
+
+// Check that we still get the right DTORs run when we let a suspended coro
+// go out of scope.
+
+#include "../coro.h"
+
+struct coro1 {
+ struct promise_type;
+ using handle_type = coro::coroutine_handle<coro1::promise_type>;
+ handle_type handle;
+ coro1 () : handle(0) {}
+ coro1 (handle_type _handle)
+ : handle(_handle) {
+ PRINT("Created coro1 object from handle");
+ }
+ coro1 (const coro1 &) = delete; // no copying
+ coro1 (coro1 &&s) : handle(s.handle) {
+ s.handle = nullptr;
+ PRINT("coro1 mv ctor ");
+ }
+ coro1 &operator = (coro1 &&s) {
+ handle = s.handle;
+ s.handle = nullptr;
+ PRINT("coro1 op= ");
+ return *this;
+ }
+ ~coro1() {
+ printf ("Destroyed coro1\n");
+ if ( handle )
+ handle.destroy();
+ }
+
+ struct suspend_never_prt {
+ bool await_ready() const noexcept { return true; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-never-susp");}
+ void await_resume() const noexcept { PRINT ("susp-never-resume");}
+ ~suspend_never_prt() {};
+ };
+
+ struct suspend_always_prt {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(handle_type) const noexcept { PRINT ("susp-always-susp");}
+ void await_resume() const noexcept { PRINT ("susp-always-resume");}
+ ~suspend_always_prt() { printf ("Destroyed suspend_always_prt\n"); }
+ };
+
+ struct promise_type {
+ promise_type() { PRINT ("Created Promise"); }
+ ~promise_type() { printf ("Destroyed Promise\n"); }
+
+ auto get_return_object () {
+ PRINT ("get_return_object: handle from promise");
+ return handle_type::from_promise (*this);
+ }
+ auto initial_suspend () {
+ PRINT ("get initial_suspend (always)");
+ return suspend_always_prt{};
+ }
+ auto final_suspend () {
+ PRINT ("get final_suspend (always)");
+ return suspend_always_prt{};
+ }
+ void return_void () {
+ PRINT ("return_void ()");
+ }
+
+ void unhandled_exception() { PRINT ("** unhandled exception"); }
+ };
+};
+
+struct coro1
+f () noexcept
+{
+ PRINT ("coro1: about to return");
+ co_return;
+}
+
+int main ()
+{
+ PRINT ("main: create coro1");
+ struct coro1 x = f ();
+ PRINT ("main: got coro1 - resuming");
+ if (x.handle.done())
+ {
+ PRINT ("main: f() should be suspended, says it's done");
+ abort();
+ }
+
+#if __has_builtin (__builtin_coro_suspended)
+ if (! __builtin_coro_suspended(handle))
+ {
+ PRINT ("main: f() should be suspended, but says it isn't");
+ abort();
+ }
+#endif
+
+ /* We are suspended... so let everything out of scope and therefore
+ destroy it. */
+
+ puts ("main: returning");
+ return 0;
+}
--- /dev/null
+// { dg-do compile }
+
+// Test that we compile the simple case described in PR 92933
+
+#include "../coro.h"
+
+#define RETURN_VOID
+#include "../coro1-ret-int-yield-int.h"
+
+struct some_error {};
+
+coro1
+foo() {
+ try {
+ co_return;
+ } catch (some_error) {
+ }
+}
extern gimple_opt_pass *make_pass_warn_nonnull_compare (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_sprintf_length (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_walloca (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_coroutine_lower_builtins (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_coroutine_early_expand_ifns (gcc::context *ctxt);
/* IPA Passes */
extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);
+2020-01-18 Iain Sandoe <iain@sandoe.co.uk>
+
+ * include/Makefile.am: Add coroutine to the std set.
+ * include/Makefile.in: Regenerated.
+ * include/std/coroutine: New file.
+
2020-01-17 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/92376
${std_srcdir}/complex \
${std_srcdir}/concepts \
${std_srcdir}/condition_variable \
+ ${std_srcdir}/coroutine \
${std_srcdir}/deque \
${std_srcdir}/execution \
${std_srcdir}/filesystem \
${std_srcdir}/complex \
${std_srcdir}/concepts \
${std_srcdir}/condition_variable \
+ ${std_srcdir}/coroutine \
${std_srcdir}/deque \
${std_srcdir}/execution \
${std_srcdir}/filesystem \
--- /dev/null
+// <coroutine> -*- C++ -*-
+
+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/coroutine
+ * This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_COROUTINE
+#define _GLIBCXX_COROUTINE 1
+
+#pragma GCC system_header
+
+// It is very likely that earlier versions would work, but they are untested.
+#if __cplusplus >= 201402L
+
+#include <bits/c++config.h>
+
+/**
+ * @defgroup coroutines Coroutines
+ *
+ * Components for supporting coroutine implementations.
+ */
+
+#if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L
+# include <compare>
+# define _COROUTINES_USE_SPACESHIP 1
+#else
+# include <bits/stl_function.h> // for std::less
+# define _COROUTINES_USE_SPACESHIP 0
+#endif
+
+namespace std _GLIBCXX_VISIBILITY (default)
+{
+ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#if __cpp_coroutines
+ inline namespace __n4835 {
+
+ // 17.12.2 coroutine traits
+ /// [coroutine.traits]
+ /// [coroutine.traits.primary]
+ template <typename _Result, typename...>
+ struct coroutine_traits
+ {
+ using promise_type = typename _Result::promise_type;
+ };
+
+ // 17.12.3 Class template coroutine_handle
+ /// [coroutine.handle]
+ template <typename _Promise = void>
+ struct coroutine_handle;
+
+ template <> struct
+ coroutine_handle<void>
+ {
+ public:
+ // 17.12.3.1, construct/reset
+ constexpr coroutine_handle() noexcept : _M_fr_ptr(0) {}
+
+ constexpr coroutine_handle(std::nullptr_t __h) noexcept
+ : _M_fr_ptr(__h)
+ {}
+
+ coroutine_handle& operator=(std::nullptr_t) noexcept
+ {
+ _M_fr_ptr = nullptr;
+ return *this;
+ }
+
+ public:
+ // 17.12.3.2, export/import
+ constexpr void* address() const noexcept { return _M_fr_ptr; }
+
+ constexpr static coroutine_handle from_address(void* __a) noexcept
+ {
+ coroutine_handle __self;
+ __self._M_fr_ptr = __a;
+ return __self;
+ }
+
+ public:
+ // 17.12.3.3, observers
+ constexpr explicit operator bool() const noexcept
+ {
+ return bool(_M_fr_ptr);
+ }
+
+ bool done() const noexcept { return __builtin_coro_done(_M_fr_ptr); }
+
+ // 17.12.3.4, resumption
+ void operator()() const { resume(); }
+
+ void resume() const { __builtin_coro_resume(_M_fr_ptr); }
+
+ void destroy() const { __builtin_coro_destroy(_M_fr_ptr); }
+
+ protected:
+ void* _M_fr_ptr;
+ };
+
+ // 17.12.3.6 Comparison operators
+ /// [coroutine.handle.compare]
+ constexpr bool operator==(coroutine_handle<> __a,
+ coroutine_handle<> __b) noexcept
+ {
+ return __a.address() == __b.address();
+ }
+
+#if _COROUTINES_USE_SPACESHIP
+ constexpr strong_ordering
+ operator<=>(coroutine_handle<> __a, coroutine_handle<> __b) noexcept
+ { return std::compare_three_way()(__a.address(), __b.address()); }
+#else
+ // These are to enable operation with std=c++14,17.
+ constexpr bool operator!=(coroutine_handle<> __a,
+ coroutine_handle<> __b) noexcept
+ {
+ return !(__a == __b);
+ }
+
+ constexpr bool operator<(coroutine_handle<> __a,
+ coroutine_handle<> __b) noexcept
+ {
+ return less<void*>()(__a.address(), __b.address());
+ }
+
+ constexpr bool operator>(coroutine_handle<> __a,
+ coroutine_handle<> __b) noexcept
+ {
+ return __b < __a;
+ }
+
+ constexpr bool operator<=(coroutine_handle<> __a,
+ coroutine_handle<> __b) noexcept
+ {
+ return !(__a > __b);
+ }
+
+ constexpr bool operator>=(coroutine_handle<> __a,
+ coroutine_handle<> __b) noexcept
+ {
+ return !(__a < __b);
+ }
+#endif
+
+ template <typename _Promise>
+ struct coroutine_handle : coroutine_handle<>
+ {
+ // 17.12.3.1, construct/reset
+ using coroutine_handle<>::coroutine_handle;
+
+ static coroutine_handle from_promise(_Promise& p)
+ {
+ coroutine_handle __self;
+ __self._M_fr_ptr
+ = __builtin_coro_promise((char*) &p, __alignof(_Promise), true);
+ return __self;
+ }
+
+ coroutine_handle& operator=(std::nullptr_t) noexcept
+ {
+ coroutine_handle<>::operator=(nullptr);
+ return *this;
+ }
+
+ // 17.12.3.2, export/import
+ constexpr static coroutine_handle from_address(void* __a)
+ {
+ coroutine_handle __self;
+ __self._M_fr_ptr = __a;
+ return __self;
+ }
+
+ // 17.12.3.5, promise accesss
+ _Promise& promise() const
+ {
+ void* __t
+ = __builtin_coro_promise (this->_M_fr_ptr, __alignof(_Promise), false);
+ return *static_cast<_Promise*>(__t);
+ }
+ };
+
+ /// [coroutine.noop]
+ struct noop_coroutine_promise
+ {
+ };
+
+ void __dummy_resume_destroy() __attribute__((__weak__));
+ void __dummy_resume_destroy() {}
+
+ struct __noop_coro_frame
+ {
+ void (*__r)() = __dummy_resume_destroy;
+ void (*__d)() = __dummy_resume_destroy;
+ struct noop_coroutine_promise __p;
+ } __noop_coro_fr __attribute__((__weak__));
+
+ // 17.12.4.1 Class noop_coroutine_promise
+ /// [coroutine.promise.noop]
+ template <>
+ struct coroutine_handle<noop_coroutine_promise> : public coroutine_handle<>
+ {
+ using _Promise = noop_coroutine_promise;
+
+ public:
+ // 17.12.4.2.1, observers
+ constexpr explicit operator bool() const noexcept { return true; }
+
+ constexpr bool done() const noexcept { return false; }
+
+ // 17.12.4.2.2, resumption
+ void operator()() const noexcept {}
+
+ void resume() const noexcept {}
+
+ void destroy() const noexcept {}
+
+ // 17.12.4.2.3, promise access
+ _Promise& promise() const
+ {
+ return *static_cast<_Promise*>(
+ __builtin_coro_promise(this->_M_fr_ptr, __alignof(_Promise), false));
+ }
+
+ // 17.12.4.2.4, address
+ private:
+ friend coroutine_handle<noop_coroutine_promise> noop_coroutine() noexcept;
+
+ coroutine_handle() noexcept { this->_M_fr_ptr = (void*) &__noop_coro_fr; }
+ };
+
+ using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
+
+ inline noop_coroutine_handle noop_coroutine() noexcept
+ {
+ return noop_coroutine_handle();
+ }
+
+ // 17.12.5 Trivial awaitables
+ /// [coroutine.trivial.awaitables]
+ struct suspend_always
+ {
+ bool await_ready() { return false; }
+
+ void await_suspend(coroutine_handle<>) {}
+
+ void await_resume() {}
+ };
+
+ struct suspend_never
+ {
+ bool await_ready() { return true; }
+
+ void await_suspend(coroutine_handle<>) {}
+
+ void await_resume() {}
+ };
+
+ } // namespace __n4835
+
+#else
+#error "the coroutine header requires -fcoroutines"
+#endif
+
+ _GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+
+#endif // C++14 (we are allowing use from at least this)
+
+#endif // _GLIBCXX_COROUTINE