public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [C++ coroutines 0/6] Implement C++ coroutines.
@ 2019-11-17 10:23 Iain Sandoe
  2019-11-17 10:24 ` [C++ coroutines 1/6] Common code and base definitions Iain Sandoe
  2019-11-18 12:35 ` [C++ coroutines 0/6] Implement C++ coroutines Nathan Sidwell
  0 siblings, 2 replies; 30+ messages in thread
From: Iain Sandoe @ 2019-11-17 10:23 UTC (permalink / raw)
  To: GCC Patches, libstdc++


This patch series is an initial implementation of a coroutine feature,
expected to be standardised in C++20.

Standardisation status (and potential impact on this implementation):
----------------------

The facility was accepted into the working draft for C++20 by WG21 in
February 2019.  During two following WG21 meetings, design and national
body comments have been reviewed, with no significant change resulting.

Mature implementations (several years) of this exist in MSVC, clang and
EDG with some experience using the clang one in production - so that the
underlying principles are thought to be sound.

At this stage, the remaining potential for change comes from two areas of
national body comments that were not resolved during the last WG21 meeting:
(a) handling of the situation where aligned allocation is available.
(b) handling of the situation where a user wants coroutines, but does not
    want exceptions (e.g. a GPU).

It is not expected that the resolution to either of these will produce any
major change.

The current GCC implementation is against n4835 [1].

ABI
---

The various compiler developers have discussed a minimal ABI to allow one
implementation to call coroutines compiled by another; this amounts to:

1. The layout of a public portion of the coroutine frame.
2. A number of compiler builtins that the standard library might use.

The eventual home for the ABI is not decided yet, I will put a draft onto
the wiki this week.

The ABI has currently no target-specific content (a given psABI might elect
to mandate alignment, but the common ABI does not do this).

There is not need to add any new mangling, since the components of this are
regular functions with manipulation of the coroutine via a type-erased handle.

Standard Library impact
-----------------------

The current implementations require addition of only a single header to
the standard library (no change to the runtime).  This header is part of
the patch series.

GCC Implementation outline
--------------------------

The standard's design for coroutines does not decorate the definition of
a coroutine in any way, so that a function is only known to be a coroutine
when one of the keywords (co_await, co_yield, co_return) is encountered.

This means that we cannot special-case such functions from the outset, but
must process them differently when they are finalised - which we do from
"finish_function ()".

At a high level, this design of coroutine produces four pieces from the
original user's function:

  1. A coroutine state frame (taking the logical place of the activation
     record for a regular function).  One item stored in that state is the
     index of the current suspend point.
  2. A "ramp" function
     This is what the user calls to construct the coroutine frame and start
     the coroutine execution.  This will return some object representing the
     coroutine's eventual return value (or means to continue it when it it
     suspended).
  3. A "resume" function.
     This is what gets called when a the coroutine is resumed when suspended.
  4. A "destroy" function.
     This is what gets called when the coroutine state should be destroyed
     and its memory returned.

The standard's coroutines involve cooperation of the user's authored function
with a provided "promise" class, which includes mandatory methods for
handling the state transitions and providing output values.  Most realistic
coroutines will also have one or more 'awaiter' classes that implement the
user's actions for each suspend point.  As we parse (or during template
expansion) the types of the promise and awaiter classes become known, and can
then be verified against the signatures expected by the standard.

Once the function is parsed (and templates expanded) we are able to make the
transformation into the four pieces noted above.

The implementation here takes the approach of a series of AST transforms.
The state machine suspend points are encoded in three internal functions
(one of which represents an exit from scope without cleanups).  These three 
IFNs are lowered early in the middle end, such that the majority of GCC's
optimisers can be run on the resulting output.

As a design choice, we have carried out the outlining of the user's function
in the front end, and taken advantage of the existing middle end's abilities
to inline and DCE where that is profitable.

Since the state machine is actually common to both resumer and destroyer
functions, we make only a single function "actor" that contains both the
resume and destroy paths.  The destroy function is represented by a small
stub that sets a value to signal the use of the destroy path and calls the
actor.  The idea is that optimisation of the state machine need only be done
once - and then the resume and destroy paths can be identified allowing the
middle end's inline and DCE machinery to optimise as profitable as noted above.

The middle end components for this implementation are:
 1. Lower the coroutine builtins that allow the standard library header to
    interact with the coroutine frame (these fairly simple logical or
    numerical substitution of values given a coroutine frame pointer).
 2. Lower the IFN that represents the exit from state without cleanup.
    Essentially, this becomes a gimple goto.
 3. Lower the IFNs that represent the state machine paths for the resume and
    destroy cases.
 4. A very late pass that is able to re-size the coroutine frame when there
    are unused entries and therefore choose the minimum allocation for it.

There are no back-end implications to this current design.

GCC Implementation Status
-------------------------

The current implementation should be considered somewhat experimental and is
guarded by a "-fcoroutines" flag.  I have set out to minimise impact on the
compiler (such that with the switch off, coroutines should be a NOP).

The branch has been feature-complete for a few weeks and published on Compiler
Explorer since late September.  I have been keeping a copy of the branch on
my github page, and some bug reports have been filed there (and dealt with).

The only common resource taken is a single bit in the function decl to flag
that this function is determined to be a coroutine.

Patch Series
------------

The patch series is against r278049 (Mon 11th Nov).

There are 6 pieces to try an localise the reviewer interest areas.  However
it would not make sense to commit except as possibly two (main and testsuite).
I have not tested that the compiler would even build part-way through this
series.

1) Common code and base definitions.

This is the background content, defining the gating flag, keywords etc.

2) Builtins and internal functions.

Definitions of the builtins used by the standard library header and the
internal functions used to implement the state machine.

3)  Front end parsing and AST transforms.

This is the largest part of the code, and has essentially two phases
 1. parse (and template expansion)
 2. analysis and transformation, which does the code generation for the
    state machine.

4) Middle end expanders and transforms

 As per the description above.

5) Standard library header.

This is mostly mandated by the standard, although (of course) the decision
to implement the interaction with the coroutine frame by inline builtin
calls is pertinent.

There is no runtime addition for this (the builtins are expanded directly).

6) Testsuite.

There are two chunks of tests.
 1. those that check for correct error handling
 2. those that check for the correct lowering of the state machine
 
Since the second set are checking code-gen, they are run as 'torture' tests
with the default options list.

======

I will put this patch series onto a git branch for those that would prefer
to view it in that form.

thanks
Iain

======

[1] https://wg21.link/n4835






^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 1/6] Common code and base definitions.
  2019-11-17 10:23 [C++ coroutines 0/6] Implement C++ coroutines Iain Sandoe
@ 2019-11-17 10:24 ` Iain Sandoe
  2019-11-17 10:24   ` [C++ coroutines 2/6] Define builtins and internal functions Iain Sandoe
  2019-11-17 15:49   ` [C++ coroutines 1/6] Common code and base definitions Jeff Law
  2019-11-18 12:35 ` [C++ coroutines 0/6] Implement C++ coroutines Nathan Sidwell
  1 sibling, 2 replies; 30+ messages in thread
From: Iain Sandoe @ 2019-11-17 10:24 UTC (permalink / raw)
  To: GCC Patches, libstdc++

This part of the patch series provides the gating flag, the keywords,
cpp defines etc.

gcc/ChangeLog:

2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>

	* doc/invoke.texi: Document the fcoroutines command line
	switch.

gcc/c-family/ChangeLog:

2019-11-17  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.

gcc/cp/ChangeLog:

2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>

	* cp-tree.h (lang_decl-fn): coroutine_p, new bit.
	* lex.c (init_reswords): Enable keywords when the coroutine flag
	is set,
	* operators.def (co_await): New operator.
---
 gcc/c-family/c-common.c     |  5 +++++
 gcc/c-family/c-common.h     |  5 +++++
 gcc/c-family/c-cppbuiltin.c |  2 ++
 gcc/c-family/c.opt          |  4 ++++
 gcc/cp/cp-tree.h            | 17 ++++++++++++++++-
 gcc/cp/lex.c                |  2 ++
 gcc/cp/operators.def        |  1 +
 gcc/doc/invoke.texi         |  4 ++++
 8 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 4881199..8be92a6 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -536,6 +536,11 @@ const struct c_common_resword c_common_reswords[] =
   { "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 },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 80a8c9f..6ec0910 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -189,6 +189,9 @@ enum rid
   /* 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,
 
@@ -433,9 +436,11 @@ extern machine_mode c_default_pointer_mode;
 #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[];
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index cf3d437..6299d47 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -1000,6 +1000,8 @@ c_cpp_builtins (cpp_reader *pfile)
           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.  */
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 914a2f0..62bf4f1 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1469,6 +1469,10 @@ fconstexpr-ops-limit=
 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.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index adc021b..6fb99d8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2696,7 +2696,9 @@ struct GTY(()) lang_decl_fn {
   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.  */
 
@@ -4982,6 +4984,13 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #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) \
@@ -7892,6 +7901,12 @@ extern tree cp_ubsan_maybe_instrument_downcast	(location_t, tree, tree, tree);
 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 bool morph_fn_to_coro			(tree, tree *, tree *);
+
 /* Inline bodies.  */
 
 inline tree
diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c
index 25529e7..cd8dd80 100644
--- a/gcc/cp/lex.c
+++ b/gcc/cp/lex.c
@@ -233,6 +233,8 @@ init_reswords (void)
     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)
diff --git a/gcc/cp/operators.def b/gcc/cp/operators.def
index ee0a4c1..3e5bd1f 100644
--- a/gcc/cp/operators.def
+++ b/gcc/cp/operators.def
@@ -87,6 +87,7 @@ DEF_OPERATOR ("++", PREINCREMENT_EXPR, "pp", OVL_OP_FLAG_UNARY)
 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)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 00eb7e7..7be3bfb 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -2567,6 +2567,10 @@ of a loop too many expressions need to be evaluated, the resulting constexpr
 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
-- 
2.8.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 2/6] Define builtins and internal  functions.
  2019-11-17 10:24 ` [C++ coroutines 1/6] Common code and base definitions Iain Sandoe
@ 2019-11-17 10:24   ` Iain Sandoe
  2019-11-17 10:26     ` [C++ coroutines 3/6] Front end parsing and transforms Iain Sandoe
  2019-11-17 16:54     ` [C++ coroutines 2/6] Define builtins and internal functions Jeff Law
  2019-11-17 15:49   ` [C++ coroutines 1/6] Common code and base definitions Jeff Law
  1 sibling, 2 replies; 30+ messages in thread
From: Iain Sandoe @ 2019-11-17 10:24 UTC (permalink / raw)
  To: GCC Patches, libstdc++


This part of the patch series provides the builtin functions
used by the standard library code and the internal functions
used to implement lowering of the coroutine state machine.

gcc/ChangeLog:

2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>

	* builtin-types.def (BT_FN_BOOL_PTR): New.
	(BT_FN_PTR_PTR_SIZE_BOOL): New.
	* builtins.def (DEF_COROUTINE_BUILTIN): New.
	* coroutine-builtins.def: New file.
	* 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.
---
 gcc/builtin-types.def      |  3 +++
 gcc/builtins.def           |  9 ++++++++
 gcc/coroutine-builtins.def | 52 ++++++++++++++++++++++++++++++++++++++++++++++
 gcc/internal-fn.c          | 26 +++++++++++++++++++++++
 gcc/internal-fn.def        |  6 ++++++
 5 files changed, 96 insertions(+)
 create mode 100644 gcc/coroutine-builtins.def

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index e5c9e06..6b4875e 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -297,6 +297,7 @@ DEF_FUNCTION_TYPE_1 (BT_FN_UINT32_UINT32, BT_UINT32, BT_UINT32)
 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)
@@ -625,6 +626,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_UINT32_UINT32_PTR,
 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_SIZE_BOOL,
+		     BT_PTR, BT_PTR, BT_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)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index d8233f5..5ad9608 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -189,6 +189,12 @@ along with GCC; see the file COPYING3.  If not see
   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
@@ -1064,6 +1070,9 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE", BT_FN_INT, ATTR_NOTHROW_LEAF_LIST)
 /* 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.  */
diff --git a/gcc/coroutine-builtins.def b/gcc/coroutine-builtins.def
new file mode 100644
index 0000000..2f611e9
--- /dev/null
+++ b/gcc/coroutine-builtins.def
@@ -0,0 +1,52 @@
+/* This file contains the definitions and documentation for the
+   coroutines builtins used in GCC.
+
+   Copyright (C) 2018-2019 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_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)
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 6a878bd..85bcfee 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -2878,6 +2878,32 @@ expand_NOP (internal_fn, gcall *)
   /* 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.  */
 
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index a945944..fc54903 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -361,6 +361,12 @@ DEF_INTERNAL_FN (LAUNDER, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
 /* 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)
 
-- 
2.8.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 3/6] Front end parsing and transforms.
  2019-11-17 10:24   ` [C++ coroutines 2/6] Define builtins and internal functions Iain Sandoe
@ 2019-11-17 10:26     ` Iain Sandoe
  2019-11-17 10:26       ` [C++ coroutines 4/6] Middle end expanders " Iain Sandoe
                         ` (2 more replies)
  2019-11-17 16:54     ` [C++ coroutines 2/6] Define builtins and internal functions Jeff Law
  1 sibling, 3 replies; 30+ messages in thread
From: Iain Sandoe @ 2019-11-17 10:26 UTC (permalink / raw)
  To: GCC Patches, libstdc++


As described in the covering note, there are two parts to this.

1. Parsing, template instantiation and diagnostics for the standard-
   mandated class entries.

  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 transformatin until template expansion is
  complete so that we have complete types at that time.

2. AST analysis and transformation which performs the code-gen for the
   outlined state machine.

   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 analysed 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
     optimisations 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 instanceare 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.

gcc/cp/ChangeLog:

2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>

	* Make-lang.in: Add coroutines.o.
	* call.c (add_builtin_candidates): Handle CO_AWAIT_EXPR.
	(op_error): Likewise.
	(build_new_op_1): Likewise.
	* constexpr.c (potential_constant_expression_1): Handle
	CO_AWAIT_EXPR, CO_YIELD_EXPR.
	* coroutines.cc: New file.
	* cp-objcp-common.c (cp_common_init_ts): Add CO_AWAIT_EXPR,
	CO_YIELD_EXPR, CO_RETRN_EXPR as expressions.
	* cp-tree.def (CO_AWAIT_EXPR): New.
	(CO_YIELD_EXPR): New.
	(CO_RETRN_EXPR): 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.
	* 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): Handl 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_RETRN_EXPRs.
	* tree.c (cp_walk_subtrees): Likewise.
---
 gcc/cp/Make-lang.in      |    2 +-
 gcc/cp/call.c            |   13 +
 gcc/cp/constexpr.c       |    5 +
 gcc/cp/coroutines.cc     | 3259 ++++++++++++++++++++++++++++++++++++++++++++++
 gcc/cp/cp-objcp-common.c |    4 +
 gcc/cp/cp-tree.def       |   24 +
 gcc/cp/decl.c            |   49 +
 gcc/cp/parser.c          |   89 +-
 gcc/cp/pt.c              |   21 +
 gcc/cp/tree.c            |   31 +
 10 files changed, 3490 insertions(+), 7 deletions(-)
 create mode 100644 gcc/cp/coroutines.cc

diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in
index 8430c4c..e678462 100644
--- a/gcc/cp/Make-lang.in
+++ b/gcc/cp/Make-lang.in
@@ -73,7 +73,7 @@ CXX_C_OBJS = attribs.o incpath.o \
 # 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 \
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 0034c1c..86c155d 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -3156,6 +3156,7 @@ add_builtin_candidates (struct z_candidate **candidates, enum tree_code code,
     case ADDR_EXPR:
     case COMPOUND_EXPR:
     case COMPONENT_REF:
+    case CO_AWAIT_EXPR:
       return;
 
     case COND_EXPR:
@@ -4921,6 +4922,16 @@ op_error (const op_location_t &loc,
 		  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)
@@ -6169,6 +6180,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
 	case ADDR_EXPR:
 	case COMPOUND_EXPR:
 	case COMPONENT_REF:
+	case CO_AWAIT_EXPR:
 	  result = NULL_TREE;
 	  result_valid_p = true;
 	  break;
@@ -6456,6 +6468,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
     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:
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 20fddc5..c6f7a84 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -7385,6 +7385,11 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
     case ANNOTATE_EXPR:
       return RECUR (TREE_OPERAND (t, 0), rval);
 
+    /* coroutine await expressions are not.  */
+    case CO_AWAIT_EXPR:
+    case CO_YIELD_EXPR:
+      return false;
+
     default:
       if (objc_is_property_ref (t))
 	return false;
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
new file mode 100644
index 0000000..2dfde6b
--- /dev/null
+++ b/gcc/cp/coroutines.cc
@@ -0,0 +1,3259 @@
+/* coroutine-specific state, expansions and tests.
+
+   Copyright (C) 2018-2019 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/>.  */
+
+/* FIXME: minimise headers.. */
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "bitmap.h"
+#include "cp-tree.h"
+#include "stringpool.h"
+#include "cgraph.h"
+#include "stmt.h"
+#include "varasm.h"
+#include "stor-layout.h"
+#include "c-family/c-objc.h"
+#include "tree-inline.h"
+#include "intl.h"
+#include "tree-iterator.h"
+#include "omp-general.h"
+#include "convert.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "gomp-constants.h"
+#include "predict.h"
+#include "tree.h"
+#include "cxx-pretty-print.h"
+#include "gcc-rich-location.h"
+#include "hash-map.h"
+
+/* DEBUG remove me.  */
+extern void debug_tree (tree);
+
+static tree find_coro_traits_template_decl (location_t);
+static tree find_coro_handle_type (location_t, tree);
+static tree find_promise_type (tree);
+static tree
+lookup_promise_member (tree, const char *, location_t, bool);
+static bool coro_promise_type_found_p (tree, location_t);
+static tree build_co_await (location_t, tree, tree);
+
+/* 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 transformatin until template expansion is
+  complete so that we have complete types at that time.
+
+*/
+
+/* ================= 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.  */
+
+/* Lookup std::experimental.  */
+static tree
+find_std_experimental (location_t loc)
+{
+  /* we want std::experimental::coroutine_traits class template decl.  */
+  tree exp_name = get_identifier ("experimental");
+  tree exp_ns = lookup_qualified_name (std_node, exp_name, 0, false, false);
+
+  if (exp_ns == error_mark_node)
+    {
+      error_at (loc, "std::experimental not found");
+      return NULL_TREE;
+    }
+  return exp_ns;
+}
+
+/* Lookup the coroutine_traits template decl.
+   Instantiate that for the function signature.  */
+
+static tree
+find_coro_traits_template_decl (location_t kw)
+{
+  tree exp_ns = find_std_experimental (kw);
+  if (!exp_ns)
+    return NULL_TREE;
+
+  /* So now build up a type list for the template <R, ...>.
+     The function arg list length includes a terminating 'void' which we
+     don't want - but we use that slot for the fn return type (which we do
+     list even if it's 'void').  */
+  tree functyp = TREE_TYPE (current_function_decl);
+  tree arg_node = TYPE_ARG_TYPES (functyp);
+  tree targ = make_tree_vec (list_length (arg_node));
+  TREE_VEC_ELT (targ, 0) = TYPE_MAIN_VARIANT (TREE_TYPE (functyp));
+  unsigned p = 1;
+  while (arg_node != NULL_TREE && !VOID_TYPE_P (TREE_VALUE (arg_node)))
+    {
+      TREE_VEC_ELT (targ, p++) = TREE_VALUE (arg_node);
+      arg_node = TREE_CHAIN (arg_node);
+    }
+
+  tree traits_name = get_identifier ("coroutine_traits");
+  tree traits_decl
+    = lookup_template_class (traits_name, targ,
+			     /* in_decl */ NULL_TREE,
+			     /* context */ exp_ns,
+			     /* entering scope */ false, tf_none);
+
+  if (traits_decl == error_mark_node)
+    {
+      error_at (kw, "couldn't instantiate coroutine_traits");
+      return NULL_TREE;
+    }
+
+  return traits_decl;
+}
+
+static tree
+find_coro_handle_type (location_t kw, tree promise_type)
+{
+  tree exp_ns = find_std_experimental (kw);
+  if (!exp_ns)
+    return NULL_TREE;
+
+  /* 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_name = get_identifier ("coroutine_handle");
+  tree handle_type
+    = lookup_template_class (handle_name, targ,
+			     /* in_decl */ NULL_TREE,
+			     /* context */ exp_ns,
+			     /* entering scope */ false, tf_none);
+
+  if (handle_type == error_mark_node)
+    {
+      error_at (kw, "couldn't instantiate coroutine_handle for promise");
+      return NULL_TREE;
+    }
+
+  return handle_type;
+}
+
+/* Look for the promise_type in the instantiated.  */
+
+static tree
+find_promise_type (tree handle_type)
+{
+  tree promise_name = get_identifier ("promise_type");
+
+  tree promise_type
+    = lookup_member (handle_type, promise_name,
+		     /* 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;
+}
+
+/* The state that we collect during parsing (and template expansion) for
+   a coroutine.  */
+typedef struct coroutine_info
+{
+  tree promise_type;
+  tree handle_type;
+  tree self_h_proxy;
+  tree promise_proxy;
+  location_t first_coro_keyword;
+} coroutine_info_t;
+
+/* This is a small map, one entry per coroutine, but placed here to avoid
+   adding this overhead to every function decl.  */
+static hash_map<tree, coroutine_info_t> *fn_to_coro_info;
+
+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.  */
+
+  if (!fn_to_coro_info)
+    fn_to_coro_info = new hash_map<tree, struct coroutine_info>;
+
+  bool seen;
+  coroutine_info_t &info = fn_to_coro_info->get_or_insert (fndecl, &seen);
+
+  /* If we don't already have a current promise type, try to look it up.  */
+  if (!seen || info.promise_type == NULL_TREE)
+    {
+      /* Get the coroutine traits temple decl for the specified return and
+	 argument type list.  coroutine_traits <R, ...> */
+      tree templ_decl = find_coro_traits_template_decl (loc);
+      /* Find the promise type for that.  */
+      info.promise_type = find_promise_type (templ_decl);
+
+      /* If we don't find it, punt on the rest.  */
+      if (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.  */
+      info.handle_type = find_coro_handle_type (loc, info.promise_type);
+      if (info.handle_type == NULL_TREE)
+	return false;
+
+      /* Instantiate this, we're going to use it.  */
+      info.handle_type = complete_type_or_else (info.handle_type, fndecl);
+      /* Diagnostic would be emitted by complete_type_or_else.  */
+      if (info.handle_type == error_mark_node)
+	return false;
+
+      /* Build a proxy for a handle to "self" as the param to
+	 await_suspend() calls.  */
+      info.self_h_proxy
+	= build_lang_decl (VAR_DECL, get_identifier ("self_h.proxy"),
+			   info.handle_type);
+
+      /* Build a proxy for the promise so that we can perform lookups.  */
+      info.promise_proxy
+	= build_lang_decl (VAR_DECL, get_identifier ("promise.proxy"),
+			   info.promise_type);
+
+      /* Note where we first saw a coroutine keyword.  */
+      info.first_coro_keyword = loc;
+    }
+
+  return true;
+}
+
+/* These function assumes that the caller has verified that the state for
+   the decl has been initialised, we try to minimise work here.  */
+static tree
+get_coroutine_promise_type (tree decl)
+{
+  gcc_checking_assert (fn_to_coro_info);
+
+  coroutine_info_t *info = fn_to_coro_info->get (decl);
+  if (!info)
+    return NULL_TREE;
+  return info->promise_type;
+}
+
+static tree
+get_coroutine_handle_type (tree decl)
+{
+  gcc_checking_assert (fn_to_coro_info);
+
+  coroutine_info_t *info = fn_to_coro_info->get (decl);
+  if (!info)
+    return NULL_TREE;
+  return info->handle_type;
+}
+
+static tree
+get_coroutine_self_handle_proxy (tree decl)
+{
+  gcc_checking_assert (fn_to_coro_info);
+
+  coroutine_info_t *info = fn_to_coro_info->get (decl);
+  if (!info)
+    return NULL_TREE;
+  return info->self_h_proxy;
+}
+
+static tree
+get_coroutine_promise_proxy (tree decl)
+{
+  gcc_checking_assert (fn_to_coro_info);
+
+  coroutine_info_t *info = fn_to_coro_info->get (decl);
+  if (!info)
+    return NULL_TREE;
+  return info->promise_proxy;
+}
+
+/* Lookup a Promise member.  */
+
+static tree
+lookup_promise_member (tree fndecl, const char *member_name, location_t loc,
+		       bool musthave)
+{
+  tree pm_name = get_identifier (member_name);
+  tree promise = get_coroutine_promise_type (fndecl);
+  tree pm_memb
+    = lookup_member (promise, pm_name,
+		     /*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 %qs in %qT", member_name, 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))
+    {
+      // [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))
+    {
+      // [not constexpr specifier].
+      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))
+    {
+      // [not auto specifier].
+      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))
+    {
+      // [shall not be varargs].
+      error_at (kw_loc,
+		"%qs cannot be used in"
+		" a varargs function",
+		kw_name);
+      return false;
+    }
+
+  if (DECL_CONSTRUCTOR_P (fndecl))
+    {
+      // [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))
+    {
+      // [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 will 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_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, "return statement not allowed in coroutine;"
+		     " did you mean %<co_return%>?");
+
+  return true;
+}
+
+/*  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 is the original await expr.
+    MODE:
+      0 = regular function body co_await
+      1 = await from a co_yield
+      2 = initial await
+      3 = final await.
+*/
+static tree
+build_co_await (location_t loc, tree a, tree mode)
+{
+  /* 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,
+		"member reference base type %qT is not a"
+		" structure or union",
+		o_type);
+      return error_mark_node;
+    }
+
+  /* Check for required awaitable members and their types.  */
+  tree awrd_meth
+    = lookup_member (o_type, get_identifier ("await_ready"),
+		     /* 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, get_identifier ("await_suspend"),
+		     /* 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, get_identifier ("await_resume"),
+		     /* 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 materialise
+     '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 has constraints on its return type.  */
+  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 = TYPE_CANONICAL (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)
+    /* TODO: this isn't enough of a test.  */
+    OK = true;
+
+  if (!OK)
+    {
+      fprintf (stderr, "didn't grok the suspend return : ");
+      debug_tree (susp_return_type);
+      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 (TREE_TYPE (awrs_func)), a,
+		     e_proxy, o, awaiter_calls, mode);
+}
+
+tree
+finish_co_await_expr (location_t kw, tree expr)
+{
+  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 (expr == NULL_TREE)
+    {
+      error_at (kw, "%<co_await%> requires an expression.");
+      return error_mark_node;
+    }
+
+  if (error_operand_p (expr))
+    return error_mark_node;
+
+  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 (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;
+
+  /* The incoming cast expression might be transformed by a promise
+     'await_transform()'.  */
+  tree at_meth
+    = lookup_promise_member (current_function_decl, "await_transform", kw,
+			     false /*musthave*/);
+  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);
+
+      /* Probably it's not an error to fail here, although possibly a bit odd
+	 to find await_transform but not a valid one?  */
+      if (!at_fn || a == error_mark_node)
+	return error_mark_node;
+    }
+
+  /* Now we want to build co_await a.
+     The trailing '0' is a flag that notes this is a regular co_await.  */
+  tree op = build_co_await (kw, a, integer_zero_node);
+  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_mark_node)
+    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;
+
+  /* Belt and braces, we should never get here, the expression should be
+     required in the parser. */
+  if (expr == NULL_TREE)
+    {
+      error_at (kw, "%<co_yield%> requires an expression.");
+      return error_mark_node;
+    }
+
+  if (error_operand_p (expr))
+    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 (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_member (current_function_decl, "yield_value", kw,
+				       true /*musthave*/);
+  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().  The trailing '1' is a flag that notes
+     this co_await resulted from a co_yield.   */
+
+  tree op = build_co_await (kw, yield_call, integer_one_node);
+
+  op = build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (op), expr, op);
+  TREE_SIDE_EFFECTS (op) = 1;
+
+  return op;
+}
+
+/* placeholder; in case we really need something more than the contextual
+   checks.  */
+static tree
+check_co_return_expr (tree retval, bool *no_warning)
+{
+  *no_warning = false;
+
+  return retval;
+}
+
+/* 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 (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_RETRN_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;
+
+  bool no_warning;
+  expr = check_co_return_expr (expr, &no_warning);
+
+  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.  */
+  if (warn_return_type)
+    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_member (current_function_decl, "return_void", kw,
+				 true /*musthave*/);
+      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_member (current_function_decl, "return_value", kw,
+				 true /*musthave*/);
+      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_RETRN_EXPR, void_type_node, expr, co_ret_call);
+  TREE_NO_WARNING (expr) |= no_warning;
+  expr = maybe_cleanup_point_expr_void (expr);
+  expr = add_stmt (expr);
+  return expr;
+}
+
+/* ================= 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 analysed 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
+     optimisations 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 instanceare 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.  */
+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_ARTIFICIAL (lab) = 1;
+  DECL_IGNORED_P (lab) = 1;
+  DECL_CONTEXT (lab) = ctx;
+  return lab;
+}
+
+/* We mark our named labels as used, because we want to keep them in place
+   during development.  FIXME: Remove this before integration.  */
+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) = 1;
+  TREE_USED (lab) = 1;
+  return lab;
+}
+
+struct __proxy_replace
+{
+  tree from, to;
+};
+
+static tree
+replace_proxy (tree *here, int *do_subtree, void *d)
+{
+  struct __proxy_replace *data = (struct __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_RETRN_EXPR)
+    return NULL_TREE;
+
+  location_t loc = EXPR_LOCATION (co_ret_expr);
+  /* If expr is present it will be void, and is placed immediately before
+     the call for return_{value, void};  */
+  tree expr = TREE_OPERAND (co_ret_expr, 0);
+  tree call = TREE_OPERAND (co_ret_expr, 1);
+  tree stmt_list = NULL;
+  if (expr)
+    {
+      /* This expression must be void.  */
+      expr = maybe_cleanup_point_expr_void (expr);
+      append_to_statement_list (expr, &stmt_list);
+    }
+
+  /* Now replace the promise proxy with its real value.  */
+  struct __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);
+  /* p.return_void and p.return_value are probably void, but it's not
+     clear if that's intended to be a guarantee.  CHECKME.  */
+  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)
+{
+  struct __coro_ret_data *data = (struct __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)
+{
+  struct __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-initialised
+    the third operand is 'o' (the initialiser 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;
+
+  struct __coro_aw_data *data = (struct __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;
+	}
+    }
+
+  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); /* initialiser.  */
+  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) == 3);
+  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;
+  /* Initialise 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), get_identifier ("resume"), 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:
+      /* 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)
+{
+  struct __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, struct 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, struct __await_xform_data *xform)
+{
+  struct 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;
+
+  /* FIXME: determine if it's better to walk the co_await several times with
+     a quick test, or once with a more complex test.  */
+
+  /* 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.  */
+  struct __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 routine above 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;
+  struct __await_xform_data *xform = (struct __await_xform_data *) d;
+
+  *stmt = transform_await_expr (await_expr, xform);
+  if (*stmt == error_mark_node)
+    *do_subtree = 0;
+  return NULL_TREE;
+}
+
+typedef struct __param_info
+{
+  tree field_id;
+  vec<tree *> *body_uses;
+  tree frame_type;
+} __param_info_t;
+
+typedef struct __local_var_info
+{
+  tree field_id;
+  tree field_idx;
+  location_t def_loc;
+} __local_var_info_t;
+
+/* 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_t> *local_var_uses;
+};
+
+static tree
+transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
+{
+  struct __local_vars_transform *lvd = (struct __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 orginal 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_t &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_t &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_t *local_var_info = lvd->local_var_uses->get (var_decl);
+  if (local_var_info == NULL)
+    return NULL_TREE;
+
+  /* This is our revised 'local' i.e. a frame slot.  */
+  tree revised = local_var_info->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_t> *param_uses,
+		hash_map<tree, __local_var_info_t> *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... ??? CHECKME, this might be overly cautious.  */
+      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 ();
+
+  /* FIXME: this is development marker, remove later.  */
+  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_t &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.  */
+  struct __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 dspatcher.  */
+  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 dspatcher.  */
+  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, get_identifier ("from_address"), 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.  */
+
+  struct __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_member (orig, "return_void", loc, false /*musthave*/);
+  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 fns = lookup_name_real (delname, 0, 1, /*block_p=*/true, 0, 0);
+  vec<tree, va_gc> *arglist = make_tree_vector_single (actor_fp);
+  tree del_coro_fr = lookup_arg_dependent (delname, fns, arglist);
+  del_coro_fr
+    = build_new_function_call (del_coro_fr, &arglist, true /*complain*/);
+  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)
+{
+  const char *suspend_alt = is_final ? "final_suspend" : "initial_suspend";
+  tree setup_meth = lookup_promise_member (current_function_decl, suspend_alt,
+					   loc, true /*musthave*/);
+  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 8.3.8 3.1.  */
+
+  tree point = build_int_cst (integer_type_node, (is_final ? 3 : 2));
+  return build_co_await (loc, setup_call, point);
+}
+
+static bool
+register_await_info (tree await_expr, tree aw_type, tree aw_nam, tree susp_type,
+		     tree susp_handle_nam)
+{
+  bool seen;
+  struct 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);
+      debug_tree (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);
+  debug_tree (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;
+      else 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);
+	  if (DECL_ARTIFICIAL (tvar))
+	    {
+	      struct __susp_frame_data *data = (struct __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 initialiser 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;
+	    }
+	  else
+	    /* This wouldn't be broken, and we assume no need to replace it
+	       but (ISTM) unexpected.  */
+	    fprintf (stderr, "target expr init var real?\n");
+	}
+      else
+	{
+	  debug_tree (parm);
+	  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)
+{
+  struct __susp_frame_data *data = (struct __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 initialiser for the awaitable.  The callback
+     records captured temporaries including subtrees of initialisers.  */
+  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)
+{
+  struct __susp_frame_data *awpts = (struct __susp_frame_data *) d;
+  hash_set<tree> visited;
+  awpts->saw_awaits = 0;
+  /* When register_awaits sees an await, it walks the initialiser 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 initialisers 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 = 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);
+	  struct __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;
+  struct __susp_frame_data *awpts = (struct __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_t> *param_uses;
+  location_t loc;
+  bool param_seen;
+};
+
+static tree
+register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
+{
+  struct __param_frame_data *data = (struct __param_frame_data *) d;
+
+  if (TREE_CODE (*stmt) != PARM_DECL)
+    return NULL_TREE;
+
+  bool existed;
+  __param_info_t &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_t> *local_var_uses;
+  unsigned int nest_depth, bind_indx;
+  location_t loc;
+  bool local_var_seen;
+};
+
+static tree
+register_local_var_uses (tree *stmt, int *do_subtree, void *d)
+{
+  struct __local_vars_frame_data *lvd = (struct __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_t &local_var
+	    = lvd->local_var_uses->get_or_insert (lvar, &existed);
+	  if (existed)
+	    {
+	      fprintf (stderr, "duplicate lvar: ");
+	      debug_tree (lvar);
+	      gcc_checking_assert (!existed);
+	    }
+	  tree lvtype = TREE_TYPE (lvar);
+	  tree lvname = DECL_NAME (lvar);
+	  /* Make names depth+index unique, so that we can support nested
+	     scopes with identically named locals.  */
+	  size_t namsize = sizeof ("__lv...") + IDENTIFIER_LENGTH (lvname) + 18;
+	  char *buf = (char *) alloca (namsize);
+	  snprintf (buf, namsize, "__lv.%u.%u.%s", lvd->bind_indx,
+		    lvd->nest_depth, IDENTIFIER_POINTER (lvname));
+	  /* 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.field_idx = NULL_TREE;
+	  /* We don't walk any of the local var sub-trees, they won't contain
+	     any bind exprs - well, CHECKME, but I don't think so...  */
+	}
+      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)(struct _R_frame *);
+  void (*__destroy)(struct _R_frame *);
+  struct 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)
+{
+  if (!orig || TREE_CODE (orig) != FUNCTION_DECL)
+    return false;
+
+  gcc_assert (orig == current_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.  */
+
+  suspend_points = hash_map<tree, struct suspend_point_info>::create_ggc (11);
+
+  /* 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.  */
+  struct __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 optimised 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_t> *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_t>;
+      tree arg;
+      for (arg = DECL_ARGUMENTS (orig); arg != NULL; arg = DECL_CHAIN (arg))
+	{
+	  bool existed;
+	  __param_info_t &parm = param_uses->get_or_insert (arg, &existed);
+	  gcc_checking_assert (!existed);
+	  parm.field_id = NULL_TREE;
+	  parm.body_uses = NULL;
+	}
+
+      struct __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, &param_data, NULL);
+
+      /* If no uses were seen, act as if there were no params.  */
+      if (!param_data.param_seen)
+	{
+	  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_t> local_var_uses;
+  struct __local_vars_frame_data local_vars_data
+    = {&field_list, &local_var_uses, 0, 0, fn_start, 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;
+
+  /* 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;
+
+  /* FIXME: this is development marker, remove later.  */
+  tree ramp_label
+    = create_named_label_with_ctx (fn_start, "ramp.start", current_scope ());
+  ramp_label = build_stmt (fn_start, LABEL_EXPR, ramp_label);
+  add_stmt (ramp_label);
+
+  /* The decl_expr for the coro frame pointer, initialise 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 unitialised" 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 behaviour 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 initialise 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_member (orig, "get_return_object_on_allocation_failure",
+			     fn_start, false /*musthave*/);
+
+  /* Allocate the frame.  This is a place-holder which we might alter or lower
+     in some special way after the full contents of the frame are known.  As
+     noted above we pass the frame pointer, so that the internal function can
+     get access to the frame type info.  */
+  tree resizeable
+    = build_call_expr_internal_loc (fn_start, IFN_CO_FRAME, size_type_node, 2,
+				    TYPE_SIZE_UNIT (coro_frame_type), coro_fp);
+  tree grooaf = NULL_TREE;
+  tree new_fn = NULL_TREE;
+  tree nwname = ovl_op_identifier (false, NEW_EXPR);
+  tree fns = lookup_name_real (nwname, 0, 1, /*block_p=*/true, 0, 0);
+  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, true /*complain*/, false);
+      arglist->quick_push (nth_ns);
+      new_fn = lookup_arg_dependent (nwname, fns, arglist);
+    }
+  else
+    new_fn = lookup_arg_dependent (nwname, fns, arglist);
+
+  new_fn = build_new_function_call (new_fn, &arglist, true /*complain*/);
+  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_t &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 (classtype_has_move_assign_or_move_ctor_p (
+		    parm.frame_type, true /* user-declared */))
+		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, TREE_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);
+	}
+    }
+
+  /* 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_member (orig, "get_return_object", fn_start,
+					 true /*musthave*/);
+  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);
+
+  /* Initialise 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;
+    }
+
+  const char *ueh_name = "unhandled_exception";
+  if (flag_exceptions)
+    {
+      tree ueh_meth
+	= lookup_promise_member (orig, ueh_name, fn_start, true /*musthave*/);
+      /* 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_member (orig, ueh_name, fn_start, false /*musthave*/);
+      if (!ueh_meth || ueh_meth == error_mark_node)
+	warning_at (fn_start, 0, "no member named %qs in %qT", ueh_name,
+		    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;
+
+  return true;
+}
diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index b9bc2c6..f067063 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -539,6 +539,10 @@ cp_common_init_ts (void)
   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_RETRN_EXPR);
+
   c_common_init_ts ();
 }
 
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 4e798e3..53ca6bc 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -574,6 +574,30 @@ DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2)
    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_RETRN_EXPR, "co_return", tcc_expression, 2)
+
 /*
 Local variables:
 mode:c
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 5c5a85e..b31550a 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -16680,6 +16680,29 @@ add_return_star_this_fixit (gcc_rich_location *richloc, tree fndecl)
 				       indent);
 }
 
+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);
+  /* Things get weird if we don't start fresh.  */
+  clear_fold_cache ();
+  cp_fold_function (helper);
+  DECL_CONTEXT (DECL_RESULT (helper)) = helper;
+  BLOCK_SUPERCONTEXT (DECL_INITIAL (helper)) = helper;
+  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
@@ -16692,6 +16715,10 @@ finish_function (bool inline_p)
 {
   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.  */
@@ -16717,6 +16744,21 @@ finish_function (bool inline_p)
      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);
+	  return fndecl;
+	}
+
+      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))
@@ -16956,6 +16998,13 @@ finish_function (bool inline_p)
   if (!processing_template_decl && !DECL_IMMEDIATE_FUNCTION_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.  */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 1c95d7e..165490e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -177,7 +177,9 @@ enum required_token {
   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
@@ -2471,6 +2473,12 @@ static void cp_parser_function_transaction
 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,
@@ -8108,6 +8116,7 @@ cp_parser_pseudo_destructor_name (cp_parser* parser,
      postfix-expression
      ++ cast-expression
      -- cast-expression
+     await-expression
      unary-operator cast-expression
      sizeof unary-expression
      sizeof ( type-id )
@@ -8321,6 +8330,22 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 			    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;
 	}
@@ -9754,6 +9779,7 @@ cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr)
      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.
@@ -9770,6 +9796,10 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
      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
@@ -11268,6 +11298,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
 	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);
@@ -12909,6 +12940,7 @@ cp_parser_init_statement (cp_parser *parser, tree *decl)
      continue ;
      return expression [opt] ;
      return braced-init-list ;
+     coroutine-return-statement;
      goto identifier ;
 
    GNU extension:
@@ -12979,6 +13011,7 @@ cp_parser_jump_statement (cp_parser* parser)
       cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
       break;
 
+    case RID_CO_RETURN:
     case RID_RETURN:
       {
 	tree expr;
@@ -12996,8 +13029,11 @@ cp_parser_jump_statement (cp_parser* parser)
 	  /* 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);
@@ -15354,22 +15390,25 @@ cp_parser_operator (cp_parser* parser, location_t start_loc)
     {
     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);
@@ -26031,6 +26070,41 @@ cp_parser_throw_expression (cp_parser* parser)
   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.
@@ -30167,6 +30241,9 @@ cp_parser_required_error (cp_parser *parser,
       case RT_TRANSACTION_RELAXED:
 	gmsgid = G_("expected %<__transaction_relaxed%>");
 	break;
+      case RT_CO_YIELD:
+	gmsgid = G_("expected %<co_yield%>");
+	break;
       default:
 	break;
     }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 8bacb39..2942455 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -16656,6 +16656,11 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	 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)
@@ -17522,6 +17527,22 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       finish_return_stmt (RECUR (TREE_OPERAND (t, 0)));
       break;
 
+    case CO_RETRN_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))
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index ba635d4..d296a35 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -5022,6 +5022,37 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
 	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_RETRN_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;
     }
-- 
2.8.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 4/6] Middle end expanders and transforms.
  2019-11-17 10:26     ` [C++ coroutines 3/6] Front end parsing and transforms Iain Sandoe
@ 2019-11-17 10:26       ` Iain Sandoe
  2019-11-17 10:27         ` [C++ coroutines 5/6] Standard library header Iain Sandoe
  2019-11-19 10:00         ` [C++ coroutines 4/6] Middle end expanders and transforms Richard Biener
  2019-11-18 13:23       ` [C++ coroutines 3/6] Front end parsing " Nathan Sidwell
  2019-11-19 18:40       ` Nathan Sidwell
  2 siblings, 2 replies; 30+ messages in thread
From: Iain Sandoe @ 2019-11-17 10:26 UTC (permalink / raw)
  To: GCC Patches, libstdc++


As described in the covering note, the main part of this is the
expansion of the library support builtins, these are simple boolean
or numerical substitutions.

The functionality of implementing an exit from scope without cleanup
is performed here by lowering an IFN to a gimple goto.

The final part is the expansion of the coroutine IFNs that describe the
state machine connections to the dispatchers.

   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.

gcc/ChangeLog:

2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>

	* Makefile.in: Add coroutine-passes.o.
	* coroutine-passes.cc: New file.
	* passes.def: Add pass_coroutine_lower_builtins,
	pass_coroutine_early_expand_ifns and
	pass_coroutine_finalize_frame.
	* tree-pass.h (make_pass_coroutine_lower_builtins): New.
	(make_pass_coroutine_early_expand_ifns): New.
	(make_pass_coroutine_finalize_frame): New.
---
 gcc/Makefile.in         |   1 +
 gcc/coroutine-passes.cc | 621 ++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/passes.def          |   3 +
 gcc/tree-pass.h         |   3 +
 4 files changed, 628 insertions(+)
 create mode 100644 gcc/coroutine-passes.cc

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index ac21401..fc7226a 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1266,6 +1266,7 @@ OBJS = \
 	compare-elim.o \
 	context.o \
 	convert.o \
+	coroutine-passes.o \
 	coverage.o \
 	cppbuiltin.o \
 	cppdefault.o \
diff --git a/gcc/coroutine-passes.cc b/gcc/coroutine-passes.cc
new file mode 100644
index 0000000..33e1d38
--- /dev/null
+++ b/gcc/coroutine-passes.cc
@@ -0,0 +1,621 @@
+/* coroutine expansion and optimisation passes.
+
+   Copyright (C) 2018-2019 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 jumpt 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, false /* don't re-do EH.  */);
+      *handled_ops_p = true;
+      return NULL_TREE;
+    }
+
+  /* The remaining builtins implement the library interfaces to the coro
+     frame.  */
+  tree decl = gimple_call_fndecl (stmt);
+  if (decl && fndecl_built_in_p (decl, BUILT_IN_NORMAL))
+    {
+      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_assert (TREE_CODE (align_t) == INTEGER_CST);
+	    gcc_assert (TREE_CODE (from) == INTEGER_CST);
+	    bool dir = wi::to_wide (from) != 0;
+	    tree vptr = build_pointer_type (void_type_node);
+	    HOST_WIDE_INT promise_align = TREE_INT_CST_LOW (align_t);
+	    HOST_WIDE_INT psize = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (vptr));
+	    HOST_WIDE_INT align = TYPE_ALIGN_UNIT (vptr);
+	    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, vptr, ptr,
+				build_int_cst (sizetype, 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.  */
+	    tree vptr = build_pointer_type (void_type_node);
+	    HOST_WIDE_INT psize = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (vptr));
+	    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,
+					 wide_int_to_tree (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 vptr = build_pointer_type (void_type_node);
+	    tree vpp = build_pointer_type (vptr);
+	    tree indirect
+	      = fold_build2 (MEM_REF, vpp, ptr, wide_int_to_tree (vpp, 0));
+	    tree d_ptr_tmp = make_ssa_name (TYPE_MAIN_VARIANT (vptr));
+	    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,
+				     wide_int_to_tree (vptr, 0));
+	    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 renaming 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;
+
+  /* Some of the possible YIELD points will hopefully have been removed by
+     earlier optimisations, record the ones that are present.  */
+  hash_map<int_hash<HOST_WIDE_INT, -1, -2>, tree> destinations;
+  hash_set<tree> to_remove;
+  bool changed = false;
+
+  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_ACTOR:
+	    changed = true;
+	    gsi_next (&gsi);
+	    break;
+	  case IFN_CO_YIELD:
+	    {
+	      /* .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);
+		  debug_gimple_stmt (stmt);
+		}
+	      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);
+		  debug_gimple_stmt (stmt);
+		}
+	      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
+		debug_gimple_stmt (stmt);
+	      changed = true;
+	      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;
+    }
+
+  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;
+	  }
+	if (gimple_call_internal_fn (stmt) != IFN_CO_ACTOR)
+	  gsi_next (&gsi);
+	else
+	  {
+	    if (dump_file)
+	      fprintf (dump_file, "saw CO_ACTOR in BB %u\n", bb->index);
+	    /* get yield point.  */
+	    HOST_WIDE_INT idx = TREE_INT_CST_LOW (gimple_call_arg (stmt, 0));
+	    tree *seen = destinations.get (idx);
+	    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);
+		changed = true;
+	      }
+	    /* The remove advances the iterator.  */
+	    if (gsi_end_p (gsi))
+	      break;
+	    continue;
+	  }
+      }
+
+  if (changed)
+    {
+      /* 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.  */
+		  }
+	      }
+	    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 *) { return flag_coroutines; };
+
+  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);
+}
+
+/* Optimize (not yet) and lower frame allocation.
+
+   This is a place-holder for an optimisation to remove unused frame
+   entries and re-size the frame to minimum.  */
+
+static unsigned int
+execute_finalize_frame (void)
+{
+  /* Don't rebuild stuff unless we have to. */
+  unsigned int todoflags = 0;
+  bool changed = false;
+
+  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:
+	    {
+	      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);
+	      changed = true;
+	    }
+	    break;
+	  default:
+	    gsi_next (&gsi);
+	    break;
+	  }
+      }
+
+  if (!changed)
+    {
+      if (dump_file)
+	fprintf (dump_file, "coro: nothing to do\n");
+      return todoflags;
+    }
+  else if (dump_file)
+    fprintf (dump_file, "called frame expansion for %s\n",
+	     IDENTIFIER_POINTER (DECL_NAME (current_function_decl)));
+  return 0;
+}
+
+namespace {
+
+const pass_data pass_data_coroutine_finalize_frame = {
+  GIMPLE_PASS,		 /* type */
+  "coro-finalize-frame", /* name */
+  OPTGROUP_NONE,	 /* optinfo_flags */
+  TV_NONE,		 /* tv_id */
+  (PROP_cfg | PROP_ssa), /* properties_required */
+  0,			 /* properties_provided */
+  0,			 /* properties_destroyed */
+  0,			 /* todo_flags_start */
+  0			 /* todo_flags_finish, set this in the fn. */
+};
+
+class pass_coroutine_finalize_frame : public gimple_opt_pass
+{
+public:
+  pass_coroutine_finalize_frame (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_coroutine_finalize_frame, ctxt)
+  {}
+
+  /* opt_pass methods:
+     FIXME: we should not execute this for every function, even when
+     coroutines are enabled, it should be only for the ramp - or any
+     function into which the ramp is inlined.  */
+  virtual bool gate (function *) { return flag_coroutines; };
+
+  virtual unsigned int execute (function *f ATTRIBUTE_UNUSED)
+  {
+    return execute_finalize_frame ();
+  }
+
+}; // class pass_coroutine_finalize_frame
+
+} // namespace
+
+gimple_opt_pass *
+make_pass_coroutine_finalize_frame (gcc::context *ctxt)
+{
+  return new pass_coroutine_finalize_frame (ctxt);
+}
diff --git a/gcc/passes.def b/gcc/passes.def
index 798a391..4353364 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -39,8 +39,10 @@ along with GCC; see the file COPYING3.  If not see
   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);
@@ -394,6 +396,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_cleanup_eh);
   NEXT_PASS (pass_lower_resx);
   NEXT_PASS (pass_nrv);
+  NEXT_PASS (pass_coroutine_finalize_frame);
   NEXT_PASS (pass_cleanup_cfg_post_optimizing);
   NEXT_PASS (pass_warn_function_noreturn);
   NEXT_PASS (pass_gen_hsail);
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a987661..e14abb1 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -478,6 +478,9 @@ extern gimple_opt_pass *make_pass_gen_hsail (gcc::context *ctxt);
 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);
+extern gimple_opt_pass *make_pass_coroutine_finalize_frame (gcc::context *ctxt);
 
 /* IPA Passes */
 extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);
-- 
2.8.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 5/6] Standard library header.
  2019-11-17 10:26       ` [C++ coroutines 4/6] Middle end expanders " Iain Sandoe
@ 2019-11-17 10:27         ` Iain Sandoe
  2019-11-17 10:28           ` [C++ coroutines 6/6] Testsuite Iain Sandoe
  2019-11-17 16:19           ` [C++ coroutines 5/6] Standard library header Jonathan Wakely
  2019-11-19 10:00         ` [C++ coroutines 4/6] Middle end expanders and transforms Richard Biener
  1 sibling, 2 replies; 30+ messages in thread
From: Iain Sandoe @ 2019-11-17 10:27 UTC (permalink / raw)
  To: GCC Patches, libstdc++

This provides the interfaces mandated by the standard and implements
the interaction with the coroutine frame by means of inline use of
builtins expanded at compile-time.  There should be a 1:1 correspondence
with the standard sections which are cross-referenced.

There is no runtime content.

At this stage we have the content in an inline namespace "n4835" for
the current CD.

libstdc++-v3/ChangeLog:

2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>

	* include/Makefile.am: Add coroutine to the experimental set.
	* include/Makefile.in: Regnerated.
	* include/experimental/coroutine: New file.
---
 libstdc++-v3/include/Makefile.am            |   1 +
 libstdc++-v3/include/Makefile.in            |   1 +
 libstdc++-v3/include/experimental/coroutine | 268 ++++++++++++++++++++++++++++
 3 files changed, 270 insertions(+)
 create mode 100644 libstdc++-v3/include/experimental/coroutine

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 49fd413..4ffe209 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -708,6 +708,7 @@ experimental_headers = \
 	${experimental_srcdir}/array \
 	${experimental_srcdir}/buffer \
 	${experimental_srcdir}/chrono \
+	${experimental_srcdir}/coroutine \
 	${experimental_srcdir}/deque \
 	${experimental_srcdir}/executor \
 	${experimental_srcdir}/forward_list \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index acc4fe5..fdb7d3d 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -1052,6 +1052,7 @@ experimental_headers = \
 	${experimental_srcdir}/array \
 	${experimental_srcdir}/buffer \
 	${experimental_srcdir}/chrono \
+	${experimental_srcdir}/coroutine \
 	${experimental_srcdir}/deque \
 	${experimental_srcdir}/executor \
 	${experimental_srcdir}/forward_list \
diff --git a/libstdc++-v3/include/experimental/coroutine b/libstdc++-v3/include/experimental/coroutine
new file mode 100644
index 0000000..d903352
--- /dev/null
+++ b/libstdc++-v3/include/experimental/coroutine
@@ -0,0 +1,268 @@
+// <experimental/coroutine> -*- C++ -*-
+
+// Copyright (C) 2019 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 experimental/coroutine
+ *  This is an experimental C++ Library header against the C++20 CD n4835.
+ *  @ingroup coroutine-ts
+ */
+
+#ifndef _GLIBCXX_EXPERIMENTAL_COROUTINE
+#define _GLIBCXX_EXPERIMENTAL_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>
+
+#if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L
+#  include <compare>
+#  define THE_SPACESHIP_HAS_LANDED 1
+#else
+#  include <bits/stl_function.h>
+#  define THE_SPACESHIP_HAS_LANDED 0
+#endif
+
+namespace std _GLIBCXX_VISIBILITY (default)
+{
+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#if __cpp_coroutines
+
+  namespace experimental {
+  inline namespace coroutines_n4835 {
+
+  // [coroutine.traits]
+  // [coroutine.traits.primary]
+  // 17.12.2 coroutine traits
+  template <typename _R, typename...> struct coroutine_traits
+  {
+    using promise_type = typename _R::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 : __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:
+    // 17.12.3.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:
+    // 17.12.3.3, observers
+    constexpr explicit operator bool () const noexcept
+    {
+      return bool(__fr_ptr);
+    }
+    bool done () const noexcept { return __builtin_coro_done (__fr_ptr); }
+    // 17.12.3.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;
+  };
+
+  // [coroutine.handle.compare]
+  // 17.12.3.6 Comparison operators
+  constexpr bool operator== (coroutine_handle<> __a,
+			     coroutine_handle<> __b) noexcept
+  {
+    return __a.address () == __b.address ();
+  }
+
+#if THE_SPACESHIP_HAS_LANDED
+  constexpr strong_ordering
+  operator<=> (coroutine_handle<> __a, coroutine_handle<> __b) noexcept;
+#else
+  // These are from the TS.
+  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 <class _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.__fr_ptr
+	= __builtin_coro_promise ((char *) &p, __alignof(_Promise), true);
+      return __self;
+    }
+    coroutine_handle &operator= (decltype (nullptr)) 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.__fr_ptr = __a;
+      return __self;
+    }
+    // 17.12.3.5, promise accesss
+    _Promise &promise () const
+    {
+      void *__t
+	= __builtin_coro_promise (this->__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__));
+
+  // [coroutine.promise.noop]
+  // 17.12.4.1 Class noop_coroutine_promise
+  template <>
+  class 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->__fr_ptr, __alignof(_Promise), false));
+    }
+
+    // 17.12.4.2.4, address
+    // constexpr void* address() const noexcept;
+  private:
+    friend coroutine_handle<noop_coroutine_promise> noop_coroutine () noexcept;
+
+    coroutine_handle () noexcept { this->__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 ();
+  }
+
+  // [coroutine.trivial.awaitables]
+  // 17.12.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 coroutines_n4835
+  } // namespace experimental
+
+#else
+#error "the coroutine header requires -fcoroutines"
+#endif
+
+  _GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std_GLIBCXX_VISIBILITY(default)
+
+#endif // C++14
+
+#endif // _GLIBCXX_EXPERIMENTAL_COROUTINE
-- 
2.8.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 6/6] Testsuite.
  2019-11-17 10:27         ` [C++ coroutines 5/6] Standard library header Iain Sandoe
@ 2019-11-17 10:28           ` Iain Sandoe
  2019-11-19 10:01             ` Richard Biener
  2019-11-20 11:13             ` [C++ coroutines 6/6] Testsuite JunMa
  2019-11-17 16:19           ` [C++ coroutines 5/6] Standard library header Jonathan Wakely
  1 sibling, 2 replies; 30+ messages in thread
From: Iain Sandoe @ 2019-11-17 10:28 UTC (permalink / raw)
  To: GCC Patches, libstdc++


There are two categories of test:

1. Checks for correctly formed source code and the error reporting.
2. Checks for transformation and code-gen.

The second set are run as 'torture' tests for the standard options
set, including LTO.  These are also intentionally run with no options
provided (from the coroutines.exp script).

gcc/testsuite/ChangeLog:

2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>

	* g++.dg/coroutines/co-yield-syntax-1.C: New test.
	* g++.dg/coroutines/co-yield-syntax-2.C: New test.
	* g++.dg/coroutines/co-yield-syntax-3.C: New test.
	* g++.dg/coroutines/coro-auto-fn.C: New test.
	* g++.dg/coroutines/coro-await-context-auto-fn.C: New test.
	* g++.dg/coroutines/coro-bad-return.C: New test.
	* g++.dg/coroutines/coro-builtins.C: New test.
	* g++.dg/coroutines/coro-constexpr-fn.C: New test.
	* g++.dg/coroutines/coro-context-ctor-dtor.C: New test.
	* g++.dg/coroutines/coro-context-main.C: New test.
	* g++.dg/coroutines/coro-context-vararg.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 test.
	* g++.dg/coroutines/coroutines.exp: New file.
	* g++.dg/coroutines/torture/co-await-0-triv.C: New test.
	* g++.dg/coroutines/torture/co-await-1-value.C: New test.
	* g++.dg/coroutines/torture/co-await-2-xform.C: New test.
	* g++.dg/coroutines/torture/co-await-3-rhs-op.C: New test.
	* g++.dg/coroutines/torture/co-await-4-control-flow.C: New test.
	* g++.dg/coroutines/torture/co-await-5-loop.C: New test.
	* g++.dg/coroutines/torture/co-await-6-ovl.C: New test.
	* g++.dg/coroutines/torture/co-await-7-tmpl.C: New test.
	* g++.dg/coroutines/torture/co-await-8-cascade.C: New test.
	* g++.dg/coroutines/torture/co-await-9-pair.C: New test.
	* g++.dg/coroutines/torture/co-ret-3.C: New test.
	* g++.dg/coroutines/torture/co-ret-4.C: New test.
	* g++.dg/coroutines/torture/co-ret-5.C: New test.
	* g++.dg/coroutines/torture/co-ret-6.C: New test.
	* g++.dg/coroutines/torture/co-ret-7.C: New test.
	* g++.dg/coroutines/torture/co-ret-8.C: New test.
	* g++.dg/coroutines/torture/co-ret-9.C: New test.
	* g++.dg/coroutines/torture/co-ret-void-is-ready.C: New test.
	* g++.dg/coroutines/torture/co-ret-void-is-suspend.C: New test.
	* g++.dg/coroutines/torture/co-yield-0-triv.C: New test.
	* g++.dg/coroutines/torture/co-yield-1-multi.C: New test.
	* g++.dg/coroutines/torture/co-yield-2-loop.C: New test.
	* g++.dg/coroutines/torture/co-yield-3-tmpl.C: New test.
	* g++.dg/coroutines/torture/co-yield-strings.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-0.C: New test.
	* g++.dg/coroutines/torture/func-params-1.C: New test.
	* g++.dg/coroutines/torture/func-params-2.C: New test.
	* g++.dg/coroutines/torture/func-params-3.C: New test.
	* g++.dg/coroutines/torture/func-params-4.C: New test.
	* g++.dg/coroutines/torture/func-params-5.C: New test.
	* g++.dg/coroutines/torture/gro_on_alloc_fail_0.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/co-yield-syntax-1.C          |   6 +
 .../g++.dg/coroutines/co-yield-syntax-2.C          |   6 +
 .../g++.dg/coroutines/co-yield-syntax-3.C          |  37 ++++
 gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C     |  12 ++
 .../g++.dg/coroutines/coro-await-context-auto-fn.C |  16 ++
 gcc/testsuite/g++.dg/coroutines/coro-bad-return.C  |  48 ++++++
 gcc/testsuite/g++.dg/coroutines/coro-builtins.C    |  17 ++
 .../g++.dg/coroutines/coro-constexpr-fn.C          |  12 ++
 .../g++.dg/coroutines/coro-context-ctor-dtor.C     |   8 +
 .../g++.dg/coroutines/coro-context-main.C          |   7 +
 .../g++.dg/coroutines/coro-context-vararg.C        |  13 ++
 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C |  32 ++++
 .../g++.dg/coroutines/coro-missing-promise-yield.C |  35 ++++
 .../g++.dg/coroutines/coro-missing-ret-value.C     |  34 ++++
 .../g++.dg/coroutines/coro-missing-ret-void.C      |  34 ++++
 .../g++.dg/coroutines/coro-missing-ueh-1.C         |  14 ++
 .../g++.dg/coroutines/coro-missing-ueh-2.C         |  16 ++
 .../g++.dg/coroutines/coro-missing-ueh-3.C         |  17 ++
 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h |  25 +++
 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C    |   9 +
 gcc/testsuite/g++.dg/coroutines/coro.h             | 123 ++++++++++++++
 gcc/testsuite/g++.dg/coroutines/coroutines.exp     |  50 ++++++
 .../g++.dg/coroutines/torture/co-await-0-triv.C    | 144 ++++++++++++++++
 .../g++.dg/coroutines/torture/co-await-1-value.C   | 149 ++++++++++++++++
 .../g++.dg/coroutines/torture/co-await-2-xform.C   | 155 +++++++++++++++++
 .../g++.dg/coroutines/torture/co-await-3-rhs-op.C  | 155 +++++++++++++++++
 .../coroutines/torture/co-await-4-control-flow.C   | 148 ++++++++++++++++
 .../g++.dg/coroutines/torture/co-await-5-loop.C    | 147 ++++++++++++++++
 .../g++.dg/coroutines/torture/co-await-6-ovl.C     | 153 +++++++++++++++++
 .../g++.dg/coroutines/torture/co-await-7-tmpl.C    | 149 ++++++++++++++++
 .../g++.dg/coroutines/torture/co-await-8-cascade.C | 157 +++++++++++++++++
 .../g++.dg/coroutines/torture/co-await-9-pair.C    | 155 +++++++++++++++++
 gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C | 112 ++++++++++++
 gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C | 129 ++++++++++++++
 gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C | 122 +++++++++++++
 gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C | 125 ++++++++++++++
 gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C | 114 +++++++++++++
 gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C | 126 ++++++++++++++
 gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C | 118 +++++++++++++
 .../coroutines/torture/co-ret-void-is-ready.C      | 107 ++++++++++++
 .../coroutines/torture/co-ret-void-is-suspend.C    | 111 ++++++++++++
 .../g++.dg/coroutines/torture/co-yield-0-triv.C    | 146 ++++++++++++++++
 .../g++.dg/coroutines/torture/co-yield-1-multi.C   | 159 +++++++++++++++++
 .../g++.dg/coroutines/torture/co-yield-2-loop.C    | 153 +++++++++++++++++
 .../g++.dg/coroutines/torture/co-yield-3-tmpl.C    | 160 +++++++++++++++++
 .../g++.dg/coroutines/torture/co-yield-strings.C   | 182 ++++++++++++++++++++
 .../g++.dg/coroutines/torture/coro-torture.exp     |  19 +++
 .../g++.dg/coroutines/torture/exceptions-test-0.C  | 189 +++++++++++++++++++++
 .../g++.dg/coroutines/torture/func-params-0.C      | 126 ++++++++++++++
 .../g++.dg/coroutines/torture/func-params-1.C      | 130 ++++++++++++++
 .../g++.dg/coroutines/torture/func-params-2.C      | 134 +++++++++++++++
 .../g++.dg/coroutines/torture/func-params-3.C      | 133 +++++++++++++++
 .../g++.dg/coroutines/torture/func-params-4.C      | 141 +++++++++++++++
 .../g++.dg/coroutines/torture/func-params-5.C      | 141 +++++++++++++++
 .../coroutines/torture/gro_on_alloc_fail_0.C       | 137 +++++++++++++++
 .../g++.dg/coroutines/torture/local-var-0.C        | 123 ++++++++++++++
 .../g++.dg/coroutines/torture/local-var-1.C        | 123 ++++++++++++++
 .../g++.dg/coroutines/torture/local-var-2.C        | 135 +++++++++++++++
 .../g++.dg/coroutines/torture/local-var-3.C        | 153 +++++++++++++++++
 .../g++.dg/coroutines/torture/local-var-4.C        | 162 ++++++++++++++++++
 .../coroutines/torture/mid-suspend-destruction-0.C | 127 ++++++++++++++
 61 files changed, 5920 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-builtins.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-main.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro.h
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coroutines.exp
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C

diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
new file mode 100644
index 0000000..30db0e9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
@@ -0,0 +1,6 @@
+//  { 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} }
+
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
new file mode 100644
index 0000000..71e119f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
@@ -0,0 +1,6 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int a[] = { co_yield 21 }; // { dg-error {'co_yield' cannot be used outside a function} }
+
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
new file mode 100644
index 0000000..20a5b56
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
@@ -0,0 +1,37 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+namespace coro = std::experimental;
+
+/* Diagose missing return_void() in the promise type.  */
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
new file mode 100644
index 0000000..93a04dc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
@@ -0,0 +1,12 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
new file mode 100644
index 0000000..7f4ed9a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
@@ -0,0 +1,16 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C b/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
new file mode 100644
index 0000000..e5b848d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
@@ -0,0 +1,48 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "coro.h"
+#endif
+namespace coro = std::experimental;
+
+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 "return statement not allowed" }
+{
+  if (x)
+    return Coro(); 
+  else
+    co_return;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-builtins.C b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
new file mode 100644
index 0000000..d7c4883
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
@@ -0,0 +1,17 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
new file mode 100644
index 0000000..69b109f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
@@ -0,0 +1,12 @@
+//  { 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 ();
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
new file mode 100644
index 0000000..9396432
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
@@ -0,0 +1,8 @@
+//  { 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" }
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-main.C b/gcc/testsuite/g++.dg/coroutines/coro-context-main.C
new file mode 100644
index 0000000..40d7e4e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-context-main.C
@@ -0,0 +1,7 @@
+//  { 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" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C b/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
new file mode 100644
index 0000000..55a0295
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
@@ -0,0 +1,13 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
new file mode 100644
index 0000000..8bedb77
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
@@ -0,0 +1,32 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+namespace coro = std::experimental;
+
+/* Diagose missing return_void() in the promise type.  */
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
new file mode 100644
index 0000000..95f567f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
@@ -0,0 +1,35 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+namespace coro = std::experimental;
+
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
new file mode 100644
index 0000000..fd81ce7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
@@ -0,0 +1,34 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+namespace coro = std::experimental;
+
+/* Diagose missing return_void() in the promise type.  */
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
new file mode 100644
index 0000000..17a2180
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
@@ -0,0 +1,34 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+#include "coro.h"
+
+namespace coro = std::experimental;
+
+/* 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
new file mode 100644
index 0000000..964b9f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
@@ -0,0 +1,14 @@
+//  { dg-additional-options "-fsyntax-only -fexceptions -w" }
+#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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
new file mode 100644
index 0000000..caf8fb9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
@@ -0,0 +1,16 @@
+//  { dg-additional-options "-fsyntax-only -fno-exceptions " }
+#include "coro.h"
+#include "coro-missing-ueh.h"
+
+// The missing method is warned for when exceptions are off and pedantic
+// is on (default in the testsuite).
+MissingUEH
+bar () // { dg-warning {no member named 'unhandled_exception' in} }
+{ 
+  co_return;
+}
+
+int main (int ac, char *av[]) {
+  MissingUEH x = bar ();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
new file mode 100644
index 0000000..f6ca196
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
@@ -0,0 +1,17 @@
+//  { dg-additional-options "-fsyntax-only -fno-exceptions -Wno-pedantic" }
+#include "coro.h"
+#include "coro-missing-ueh.h"
+
+/* We don't warn about the missing method, unless in pedantic mode, so
+   this compile should be clean.  */
+
+MissingUEH
+bar ()
+{ 
+  co_return;
+}
+
+int main (int ac, char *av[]) {
+  MissingUEH x = bar ();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
new file mode 100644
index 0000000..7a32354
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
@@ -0,0 +1,25 @@
+#ifndef __MissingUEH_H
+#define __MissingUEH_H
+
+namespace coro = std::experimental;
+
+/* Diagose missing unhandled_exception() in the promise type.  */
+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
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
new file mode 100644
index 0000000..f22a5e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
@@ -0,0 +1,9 @@
+// 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
diff --git a/gcc/testsuite/g++.dg/coroutines/coro.h b/gcc/testsuite/g++.dg/coroutines/coro.h
new file mode 100644
index 0000000..64df497
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro.h
@@ -0,0 +1,123 @@
+#if __has_include(<experimental/coroutine>)
+
+#include <experimental/coroutine>
+
+#else
+#ifndef __CORO_H_n4835
+#define __CORO_H_n4835
+
+// Fragments (with short-cuts) to mimic enough of the library header to
+// make some progress.
+
+#if __cpp_coroutines
+
+namespace std {
+namespace experimental {
+inline namespace coroutines_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);
+    }
+    bool suspended_p () const {
+      return __builtin_coro_is_suspended (__fr_ptr);
+    }
+  protected:
+    void *__fr_ptr;
+
+  private:
+    bool __is_suspended() const noexcept  {
+      return __builtin_coro_is_suspended (__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 std::experimental::coroutines_n4775
+
+#else
+# error "coro.h requires support for coroutines, add -fcoroutines"
+#endif
+#endif // __CORO_H_n4835
+
+#endif
diff --git a/gcc/testsuite/g++.dg/coroutines/coroutines.exp b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
new file mode 100644
index 0000000..0696cc8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
@@ -0,0 +1,50 @@
+#   Copyright (C) 2018-2019 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
new file mode 100644
index 0000000..000d083
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
@@ -0,0 +1,144 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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) {
+        puts("Created coro1 object from handle");
+  }
+  coro1 (const coro1 &) = delete; // no copying
+  coro1 (coro1 &&s) : handle(s.handle) {
+    s.handle = nullptr;
+    puts("Moved coro1");
+  }
+  coro1 &operator = (coro1 &&s) {
+    handle = s.handle;
+    s.handle = nullptr;
+    return *this;
+  }
+  ~coro1() {
+    puts("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 {
+    int 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 an int.  */
+  struct suspend_always_intprt {
+    int x;
+    suspend_always_intprt() : x(5) {}
+    ~suspend_always_intprt() {}
+    bool await_ready() const noexcept { return false; }
+    void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
+    int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
+  };
+
+  struct promise_type {
+  int 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 () %d\n",v);
+    value = v;
+  }
+
+  int get_value () { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+/* The simplest valued co_await we can do.  */
+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]");
+  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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
new file mode 100644
index 0000000..cdc7b92
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
@@ -0,0 +1,149 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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) {
+        puts("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 {
+    int 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 an int.  */
+  struct suspend_always_intprt {
+    int x;
+    suspend_always_intprt() : x(5) {}
+    ~suspend_always_intprt() {}
+    bool await_ready() const noexcept { return false; }
+    void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
+    int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
+  };
+
+  struct promise_type {
+  int 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 () %d\n",v);
+    value = v;
+  }
+
+  int get_value () { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+/* The simplest valued co_await we can do.  */
+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]");
+  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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
new file mode 100644
index 0000000..3b1bb71
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
@@ -0,0 +1,155 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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 {
+    int 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 an int.  */
+  struct suspend_always_intprt {
+    int x;
+    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
+  int 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 () %d\n",v);
+    value = v;
+  }
+
+  auto await_transform (int v) {
+    PRINTF ("await_transform an int () %d\n",v);
+    return suspend_always_intprt (v);
+  }
+
+  int get_value () { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+/* Valued with an await_transform.  */
+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]");
+  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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
new file mode 100644
index 0000000..1fb54f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
@@ -0,0 +1,155 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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 {
+    int 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 an int.  */
+  struct suspend_always_intprt {
+    int x;
+    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
+  int 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 () %d\n",v);
+    value = v;
+  }
+
+  auto await_transform (int v) {
+    PRINTF ("await_transform an int () %d\n",v);
+    return suspend_always_intprt (v);
+  }
+
+  int get_value () { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+/* Valued with an await_transform.  */
+int gX = 1;
+
+coro1
+f ()
+{
+  gX = co_await 11 + 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]");
+  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 (15) */
+  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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
new file mode 100644
index 0000000..594dfe5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
@@ -0,0 +1,148 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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 {
+    int 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 an int.  */
+  struct suspend_always_intprt {
+    int x;
+    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
+  int 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 () %d\n",v);
+    value = v;
+  }
+
+  auto await_transform (int v) {
+    PRINTF ("await_transform an int () %d\n",v);
+    return suspend_always_intprt (v);
+  }
+
+  int get_value () { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+/* 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
new file mode 100644
index 0000000..86223be
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
@@ -0,0 +1,147 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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 {
+    int 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 an int.  */
+  struct suspend_always_intprt {
+    int x;
+    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
+  int 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 () %d\n",v);
+    value = v;
+  }
+
+  auto await_transform (int v) {
+    PRINTF ("await_transform an int () %d\n",v);
+    return suspend_always_intprt (v);
+  }
+
+  int get_value () { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+/* 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();
+  if (y != 42)
+    {
+      PRINTF ("main: y is wrong : %d, should be 42\n", y);
+      //abort ();
+    }
+  puts ("main: done");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
new file mode 100644
index 0000000..fc67246
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
@@ -0,0 +1,153 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+
+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 {
+    int 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 an int.  */
+  struct suspend_always_intprt {
+    int x;
+    suspend_always_intprt() : x(5) {}
+    ~suspend_always_intprt() {}
+    bool await_ready() const noexcept { return false; }
+    void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
+    int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
+  };
+
+  struct promise_type {
+  int 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 () %d\n",v);
+    value = v;
+  }
+
+  int get_value () { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+
+  struct empty {
+    auto operator co_await() const noexcept { 
+      return suspend_always_prt{};
+    }
+  };
+};
+
+
+/* The simplest valued co_await we can do.  */
+int gX = 1;
+coro1::empty e{};
+
+coro1
+f ()
+{
+  co_await (e); /* overload */
+  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]");
+  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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
new file mode 100644
index 0000000..032e35f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
@@ -0,0 +1,149 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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 an int.  */
+  struct suspend_always_intprt {
+    T x;
+    suspend_always_intprt() : x(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; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+/* 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
new file mode 100644
index 0000000..f3504d0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
@@ -0,0 +1,157 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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 {
+    int 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 an int.  */
+  struct suspend_always_intprt {
+    int x;
+    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 * x;}
+  };
+
+  struct promise_type {
+  int 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 () %d\n",v);
+    value = v;
+  }
+
+  auto await_transform (int v) {
+    PRINTF ("await_transform an int () %d\n",v);
+    return suspend_always_intprt (v);
+  }
+
+  int get_value () { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+/* Valued with an await_transform.  */
+int gX = 1;
+coro1 f ()
+{
+  /* the await transform takes an int, the await_resume squares it.
+     so we get 11 ** 4, 14641.  */
+  gX = co_await 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] - nested");
+  f_coro.handle.resume();
+  PRINT ("main: resuming [2] - 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
new file mode 100644
index 0000000..8713adf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
@@ -0,0 +1,155 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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 {
+    int 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 an int.  */
+  struct suspend_always_intprt {
+    int x;
+    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
+    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
+  int 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 () %d\n",v);
+    value = v;
+  }
+
+  auto await_transform (int v) {
+    PRINTF ("await_transform an int () %d\n",v);
+    return suspend_always_intprt (v);
+  }
+
+  int get_value () { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+/* 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] one side of add");
+  f_coro.handle.resume();
+  PRINT ("main: resuming [1] 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
new file mode 100644
index 0000000..f57c4e9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
@@ -0,0 +1,112 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// GRO differs from the eventual return type.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#else
+#  define PRINT(X) puts(X)
+#endif
+
+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 ()");
+  }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+  //int x;
+};
+
+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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
new file mode 100644
index 0000000..d85199e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
@@ -0,0 +1,129 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// GRO differs from eventual return type and has non-trivial dtor.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#else
+#  define PRINT(X) puts(X)
+#endif
+
+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 ()");
+  }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+  //int x;
+};
+
+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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
new file mode 100644
index 0000000..0eb0055
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
@@ -0,0 +1,122 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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;
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
new file mode 100644
index 0000000..1aad1b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
@@ -0,0 +1,125 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning a T.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
new file mode 100644
index 0000000..0af0922
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
@@ -0,0 +1,114 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#else
+#  define PRINT(X) puts(X)
+#endif
+
+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");}
+  };
+
+  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 {
+  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{};
+    //return std::experimental::suspend_always{};
+  }
+  auto final_suspend () {
+    PRINT ("get final_suspend (always)");
+    return suspend_always_prt{};
+  }
+  void return_void () {
+    PRINT ("return_void ()");
+  }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+  //int x;
+};
+
+__attribute__((__noinline__))
+int foo (void) { PRINT ("called the int fn foo");  return 2; }
+
+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();
+  PRINT ("main: after resume");
+  if (!x.handle.done())
+    {
+      PRINT ("main: apparently not done...");
+      abort ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
new file mode 100644
index 0000000..f79201c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
@@ -0,0 +1,126 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test templated co-return.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#define BROKEN
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
new file mode 100644
index 0000000..b2dd776
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
@@ -0,0 +1,118 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* boolean return from await_suspend ().  */
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#else
+#  define PRINT(X) puts(X)
+#endif
+
+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 ()");
+  }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+  //int x;
+};
+
+struct coro1
+f () noexcept
+{
+  PRINT ("coro1: about to return");
+  co_return;
+}
+
+int main ()
+{
+  //__builtin_coro_promise ((void*)0, 16, true);
+  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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
new file mode 100644
index 0000000..f5662d1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
@@ -0,0 +1,107 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#else
+#  define PRINT(X) puts(X)
+#endif
+
+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 ()");
+  }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+  //int x;
+};
+
+struct coro1
+f () noexcept
+{
+  PRINT ("coro1: about to return");
+  co_return;
+}
+
+int main ()
+{
+  //__builtin_coro_promise ((void*)0, 16, true);
+  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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
new file mode 100644
index 0000000..bc2ab1e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
@@ -0,0 +1,111 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#else
+#  define PRINT(X) puts(X)
+#endif
+
+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 ()");
+  }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+  //int x;
+};
+
+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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
new file mode 100644
index 0000000..4d76a99
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
@@ -0,0 +1,146 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
new file mode 100644
index 0000000..341af61
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
@@ -0,0 +1,159 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
new file mode 100644
index 0000000..53851d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
@@ -0,0 +1,153 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+int gX ;
+
+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;
+  
+  //co_return 0;
+}
+
+int main ()
+{
+  PRINT ("main: create coro1");
+  struct coro1 f_coro = f ();
+  PRINT ("main: got coro1 - resuming (1)");
+  if (f_coro.handle.done())
+    abort();
+  f_coro.handle.resume();
+  PRINT ("main: after resume (1)");
+  int y = f_coro.handle.promise().get_value();
+
+  PRINT ("main: gX OK -- looping");
+  do {
+    //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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
new file mode 100644
index 0000000..b1659e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
@@ -0,0 +1,160 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test co_yield in templated code.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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; }
+
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+  
+  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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
new file mode 100644
index 0000000..9c953ba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
@@ -0,0 +1,182 @@
+//  { dg-do run }
+
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+#include <vector>
+#include <string>
+
+namespace coro = std::experimental;
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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; }
+
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+  
+  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 ();
+      //x.handle.resume();
+    }
+
+  if (gX != 6174)
+    {
+      PRINT ("main: apparently we didn't run mycounter DTOR...");
+      abort ();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
new file mode 100644
index 0000000..d2463b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
@@ -0,0 +1,19 @@
+# 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
new file mode 100644
index 0000000..9601564
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
@@ -0,0 +1,189 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+#include <exception>
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {
+    PRINT ("unhandled_exception: caught one!");/*exit(1);*/
+    gX = -1;
+    // returning from here should end up in final_suspend.
+    //std::terminate ();
+    }
+  };
+};
+
+// 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
new file mode 100644
index 0000000..308f7ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
@@ -0,0 +1,126 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test promise construction from function args list.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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(int x) : value (x) {  PRINTF ("Created Promise with %d\n", x); }
+  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;
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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 )
+    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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
new file mode 100644
index 0000000..4408ae1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
@@ -0,0 +1,130 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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;
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
new file mode 100644
index 0000000..2d97e76
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
@@ -0,0 +1,134 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test that we correctly re-write multiple uses of a function param
+// in the body.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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;
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
new file mode 100644
index 0000000..1242b13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
@@ -0,0 +1,133 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test that we can use a function param in a co_xxxx status.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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;
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
new file mode 100644
index 0000000..729aae6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
@@ -0,0 +1,141 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test that we can manage a constructed param copy.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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;
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+// 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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
new file mode 100644
index 0000000..e0c3d2f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
@@ -0,0 +1,141 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test that we can manage a constructed param copy.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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;
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+// 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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C b/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
new file mode 100644
index 0000000..40a3620
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
@@ -0,0 +1,137 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// check the code-gen for the failed alloc return.
+
+#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
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#else
+#  define PRINT(X) puts(X)
+#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 ()");
+  }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
new file mode 100644
index 0000000..f78787b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
@@ -0,0 +1,123 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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;
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
new file mode 100644
index 0000000..9dd7ab3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
@@ -0,0 +1,123 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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;
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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 ();
+      //x.handle.resume();
+    }
+  PRINT ("main: returning");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
new file mode 100644
index 0000000..886c913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
@@ -0,0 +1,135 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test returning an int.
+// We will use the promise to contain this to avoid having to include
+// additional C++ headers.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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;
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
new file mode 100644
index 0000000..53b7e71
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
@@ -0,0 +1,153 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test modifying a local var and yielding several instances of it.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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{};
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
new file mode 100644
index 0000000..5d4c0ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
@@ -0,0 +1,162 @@
+//  { dg-do run }
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// Test modifying a local var and yielding several instances of it.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
+
+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{};
+  }
+  int get_value (void) { return value; }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
new file mode 100644
index 0000000..6e68688
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
@@ -0,0 +1,127 @@
+// { 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.
+
+#if __clang__
+# include <experimental/coroutine>
+# include <utility>
+#else
+# include "../coro.h"
+#endif
+
+namespace coro = std::experimental;
+
+// GRO differs from the eventual return type.
+
+/* just to avoid cluttering dump files. */
+extern "C" int puts (const char *);
+extern "C" int printf (const char *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#else
+#  define PRINT(X) puts(X)
+#endif
+
+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 ()");
+  }
+  // Placeholder to satisfy parser, not doing exceptions yet.
+  void unhandled_exception() {  /*exit(1);*/ }
+  };
+};
+
+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 (! x.handle.suspended_p())
+    {
+      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;
+}
-- 
2.8.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 1/6] Common code and base definitions.
  2019-11-17 10:24 ` [C++ coroutines 1/6] Common code and base definitions Iain Sandoe
  2019-11-17 10:24   ` [C++ coroutines 2/6] Define builtins and internal functions Iain Sandoe
@ 2019-11-17 15:49   ` Jeff Law
  2020-01-09 12:36     ` [C++ coroutines 1/7] " Iain Sandoe
  1 sibling, 1 reply; 30+ messages in thread
From: Jeff Law @ 2019-11-17 15:49 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches, libstdc++

On 11/17/19 3:24 AM, Iain Sandoe wrote:
> This part of the patch series provides the gating flag, the keywords,
> cpp defines etc.
> 
> gcc/ChangeLog:
> 
> 2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
> 
> 	* doc/invoke.texi: Document the fcoroutines command line
> 	switch.
> 
> gcc/c-family/ChangeLog:
> 
> 2019-11-17  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.
> 
> gcc/cp/ChangeLog:
> 
> 2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
> 
> 	* cp-tree.h (lang_decl-fn): coroutine_p, new bit.
> 	* lex.c (init_reswords): Enable keywords when the coroutine flag
> 	is set,
> 	* operators.def (co_await): New operator.
Looks quite reasonable to me.  If you need minor twiddling due to
reviewer feedback elsewhere those are pre-approved as well.

jeff

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 5/6] Standard library header.
  2019-11-17 10:27         ` [C++ coroutines 5/6] Standard library header Iain Sandoe
  2019-11-17 10:28           ` [C++ coroutines 6/6] Testsuite Iain Sandoe
@ 2019-11-17 16:19           ` Jonathan Wakely
  2020-01-09 12:39             ` [C++ coroutines 5/7, v2] " Iain Sandoe
  1 sibling, 1 reply; 30+ messages in thread
From: Jonathan Wakely @ 2019-11-17 16:19 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches, libstdc++

On 17/11/19 10:27 +0000, Iain Sandoe wrote:
>This provides the interfaces mandated by the standard and implements
>the interaction with the coroutine frame by means of inline use of
>builtins expanded at compile-time.  There should be a 1:1 correspondence
>with the standard sections which are cross-referenced.
>
>There is no runtime content.
>
>At this stage we have the content in an inline namespace "n4835" for
>the current CD.
>
>libstdc++-v3/ChangeLog:
>
>2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
>
>	* include/Makefile.am: Add coroutine to the experimental set.
>	* include/Makefile.in: Regnerated.

"Regnerated" typo.

>	* include/experimental/coroutine: New file.
>---
> libstdc++-v3/include/Makefile.am            |   1 +
> libstdc++-v3/include/Makefile.in            |   1 +
> libstdc++-v3/include/experimental/coroutine | 268 ++++++++++++++++++++++++++++
> 3 files changed, 270 insertions(+)
> create mode 100644 libstdc++-v3/include/experimental/coroutine
>
>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index 49fd413..4ffe209 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -708,6 +708,7 @@ experimental_headers = \
> 	${experimental_srcdir}/array \
> 	${experimental_srcdir}/buffer \
> 	${experimental_srcdir}/chrono \
>+	${experimental_srcdir}/coroutine \

The experimental dir is (currently) only used for TS headers. All
C++20 support is currently experimental, so adding <coroutines> where
<concepts> and <ranges> have been added would be OK.

But I'm not really clear if this is an implementation of the TS or the
C++20 feature.  If it's a hybrid, putting it in
<experimental/coroutines> is fine.

When the final <coroutines> header is added it will need to be in
libsupc++ so that it's included for freestanding builds (and at that
point it won't be able to use <bits/stl_function.h>, but that will be
OK as the final header will be C++20-only and can rely on <compare>
unconditionally, which is also freestanding).

> 	${experimental_srcdir}/deque \
> 	${experimental_srcdir}/executor \
> 	${experimental_srcdir}/forward_list \


>diff --git a/libstdc++-v3/include/experimental/coroutine b/libstdc++-v3/include/experimental/coroutine
>new file mode 100644
>index 0000000..d903352
>--- /dev/null
>+++ b/libstdc++-v3/include/experimental/coroutine
>@@ -0,0 +1,268 @@
>+// <experimental/coroutine> -*- C++ -*-
>+
>+// Copyright (C) 2019 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 experimental/coroutine
>+ *  This is an experimental C++ Library header against the C++20 CD n4835.
>+ *  @ingroup coroutine-ts

The coroutine-ts doc group should be defined somewhere.

>+ */
>+
>+#ifndef _GLIBCXX_EXPERIMENTAL_COROUTINE
>+#define _GLIBCXX_EXPERIMENTAL_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>
>+
>+#if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L
>+#  include <compare>
>+#  define THE_SPACESHIP_HAS_LANDED 1

This is in trunk now, although not supported by Clang, and not
supported by GCC pre-C++20, so the fallback is OK.

The macro name should be a reserved name though, e.g.
_THE_SPACESHIP_HAS_LANDED

>+#else
>+#  include <bits/stl_function.h>
>+#  define THE_SPACESHIP_HAS_LANDED 0
>+#endif
>+
>+namespace std _GLIBCXX_VISIBILITY (default)
>+{
>+  _GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+#if __cpp_coroutines
>+
>+  namespace experimental {
>+  inline namespace coroutines_n4835 {

This should be a reserved name too, e.g. __coroutines_n4835.

>+
>+  // [coroutine.traits]
>+  // [coroutine.traits.primary]
>+  // 17.12.2 coroutine traits
>+  template <typename _R, typename...> struct coroutine_traits
>+  {
>+    using promise_type = typename _R::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 : __fr_ptr (0) {}

The libstdc++ naming convention is _M_xxx for non-static members (both
data members and member functions) and _S_xxx for static members
(again, both data member and functions).

This helps to distinguish members from other uglified names like
function parameters and local variables.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 2/6] Define builtins and internal functions.
  2019-11-17 10:24   ` [C++ coroutines 2/6] Define builtins and internal functions Iain Sandoe
  2019-11-17 10:26     ` [C++ coroutines 3/6] Front end parsing and transforms Iain Sandoe
@ 2019-11-17 16:54     ` Jeff Law
  2020-01-09 12:36       ` [C++ coroutines 2/7, v2] " Iain Sandoe
  1 sibling, 1 reply; 30+ messages in thread
From: Jeff Law @ 2019-11-17 16:54 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches, libstdc++

On 11/17/19 3:24 AM, Iain Sandoe wrote:
> 
> This part of the patch series provides the builtin functions
> used by the standard library code and the internal functions
> used to implement lowering of the coroutine state machine.
> 
> gcc/ChangeLog:
> 
> 2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
> 
> 	* builtin-types.def (BT_FN_BOOL_PTR): New.
> 	(BT_FN_PTR_PTR_SIZE_BOOL): New.
> 	* builtins.def (DEF_COROUTINE_BUILTIN): New.
> 	* coroutine-builtins.def: New file.
> 	* 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.
This is OK as would be any minor adjustments you may ultimately need due
to other feedback on the kit.

jeff

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 0/6] Implement C++ coroutines.
  2019-11-17 10:23 [C++ coroutines 0/6] Implement C++ coroutines Iain Sandoe
  2019-11-17 10:24 ` [C++ coroutines 1/6] Common code and base definitions Iain Sandoe
@ 2019-11-18 12:35 ` Nathan Sidwell
  1 sibling, 0 replies; 30+ messages in thread
From: Nathan Sidwell @ 2019-11-18 12:35 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches, libstdc++

On 11/17/19 5:23 AM, Iain Sandoe wrote:
> 
> This patch series is an initial implementation of a coroutine feature,
> expected to be standardised in C++20.

Yay!

> ABI
> ---

> There is not need to add any new mangling, since the components of this are
> regular functions with manipulation of the coroutine via a type-erased handle.

Ahem:
> --- a/gcc/cp/operators.def
> +++ b/gcc/cp/operators.def
> @@ -87,6 +87,7 @@ DEF_OPERATOR ("++", PREINCREMENT_EXPR, "pp", OVL_OP_FLAG_UNARY)
>  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)

That looks mangly to me.  But perhaps such manglings are unreachable.

nathan

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 3/6] Front end parsing and transforms.
  2019-11-17 10:26     ` [C++ coroutines 3/6] Front end parsing and transforms Iain Sandoe
  2019-11-17 10:26       ` [C++ coroutines 4/6] Middle end expanders " Iain Sandoe
@ 2019-11-18 13:23       ` Nathan Sidwell
  2019-11-19 18:40       ` Nathan Sidwell
  2 siblings, 0 replies; 30+ messages in thread
From: Nathan Sidwell @ 2019-11-18 13:23 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches, libstdc++

On 11/17/19 5:25 AM, Iain Sandoe wrote:


> +++ b/gcc/cp/coroutines.cc

> +/* FIXME: minimise headers.. */
FIXED?

> +/* DEBUG remove me.  */
> +extern void debug_tree (tree);
?


> +static tree find_coro_traits_template_decl (location_t);
> +static tree find_coro_handle_type (location_t, tree);
> +static tree find_promise_type (tree);
> +static tree
> +lookup_promise_member (tree, const char *, location_t, bool);

bad line break?


> +/* Lookup std::experimental.  */
> +static tree
> +find_std_experimental (location_t loc)

Would this be better caching into a global_tree?
> +{
> +  /* we want std::experimental::coroutine_traits class template decl.  */
... or is the template_decl the thing to cache? (Oh I see this comment 
is incomplete as there's promise_type too).

> +/* Lookup the coroutine_traits template decl.
> +   Instantiate that for the function signature.  */
> +
> +static tree
> +find_coro_traits_template_decl (location_t kw)
> +{

> +
> +  tree traits_name = get_identifier ("coroutine_traits");
> +  tree traits_decl
> +    = lookup_template_class (traits_name, targ,
> +			     /* in_decl */ NULL_TREE,
> +			     /* context */ exp_ns,
> +			     /* entering scope */ false, tf_none);

You should be able to pass the TEMPLATE_DECL into lookup_template_class. 
So, yes cache the TEMPLATE_DECL std::experimental::coroutine_traits on 
first lookup into a global tree.

> +
> +  if (traits_decl == error_mark_node)
> +    {
> +      error_at (kw, "couldn't instantiate coroutine_traits");

couldn't -> cannot (or something else non-apostrophey)

> +static tree
> +find_coro_handle_type (location_t kw, tree promise_type)
> +{
> +  tree exp_ns = find_std_experimental (kw);
> +  if (!exp_ns)
> +    return NULL_TREE;
> +
> +  /* 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_name = get_identifier ("coroutine_handle");
> +  tree handle_type
> +    = lookup_template_class (handle_name, targ,

As with the traits, cache the TEMPLATE_DECL.

> +/* Look for the promise_type in the instantiated.  */
> +
> +static tree
> +find_promise_type (tree handle_type)
> +{
> +  tree promise_name = get_identifier ("promise_type");

could you add these identifiers to cp_global_trees? (use-once 
identifiers fine not to, but there;s no point continually rehashing 
multi-use ones).

> +/* The state that we collect during parsing (and template expansion) for
> +   a coroutine.  */
> +typedef struct coroutine_info
> +{
> +  tree promise_type;
> +  tree handle_type;
> +  tree self_h_proxy;
> +  tree promise_proxy;
> +  location_t first_coro_keyword;
> +} coroutine_info_t;

typedef struct X{} X_t; is C-like.  Please comment the fields though.
Doesn't this need GTYing?  What keeps those trees alive across GC 
otherwise?  (try running with gc-always and see what happens?)

> +/* These function assumes that the caller has verified that the state for

function->functions

> +   the decl has been initialised, we try to minimise work here.  */

IIUC american/oxford-english IZED?

> +static tree
> +get_coroutine_promise_type (tree decl)
> +{
> +  gcc_checking_assert (fn_to_coro_info);
> +
> +  coroutine_info_t *info = fn_to_coro_info->get (decl);
> +  if (!info)
> +    return NULL_TREE;
> +  return info->promise_type;

idiomatic C++ would be

if (coroutine_info_t *info = ...)
   return info->promis_type;
return NULL_TREE;

your call.

> +/* Here we check the constraints that are common to all keywords (since the
> +   presence of a coroutine keyword makes the function into a coroutine).  */
> +

> +  /* This is arranged in order of prohibitions in the std.  */

could you add a [clause.subname] reference maybe?

> +  if (DECL_MAIN_P (fndecl))


> +/* Here we will check the constraints that are not per keyword.  */

will-> ""

> +
> +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.
> +  */
unfortunate line break?


> +  gcc_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, "return statement not allowed in coroutine;"
<%return%> not allowed ...?


> +/*  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 is the original await expr.
> +    MODE:
> +      0 = regular function body co_await
> +      1 = await from a co_yield
> +      2 = initial await
> +      3 = final await.
> +*/

Would MODE be better as an enum, from whence you cons up the INTEGER_CST 
you need for the calls? I'll bet that makes calling code clearer.  MODE 
probably not a good name, given it's usual meaning of machine_mode.

> +static tree
> +build_co_await (location_t loc, tree a, tree mode)
> +{

> +  tree o_type = complete_type_or_else (TREE_TYPE (o), o);
> +  if (TREE_CODE (o_type) != RECORD_TYPE)
> +    {
> +      error_at (loc,
> +		"member reference base type %qT is not a"
> +		" structure or union",
"member reference base type" sounds confusing to me.

> +  /* 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 materialise
ise->ize?

> +  /* The suspend method has constraints on its return type.  */
hm, as 'constraint' is now a term of art wrt concepts, perhaps 
'requirements'?


> +  bool OK = false;
OK ->ok?
> +  tree susp_return_type = TYPE_CANONICAL (TREE_TYPE (TREE_TYPE (awsp_func)));
Why are you looking at TYPE_CANONICAL, that seems fishy.

> +  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)
> +    /* TODO: this isn't enough of a test.  */
What would complete it?

> +  if (!OK)
> +    {
> +      fprintf (stderr, "didn't grok the suspend return : ");
> +      debug_tree (susp_return_type);

what's going on here?

> +tree
> +finish_co_await_expr (location_t kw, tree expr)
> +{

> +
> +  if (expr == NULL_TREE)
> +    {
> +      error_at (kw, "%<co_await%> requires an expression.");
isn't this a syntactic restriction?  How can we get here without already 
going wrong?


> +  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);
> +
> +      /* Probably it's not an error to fail here, although possibly a bit odd
> +	 to find await_transform but not a valid one?  */

Is this comment still accurate? I don't think this is any kind of 
SFINAE-like context, and we will have emitted an error.


> +  /* Belt and braces, we should never get here, the expression should be
> +     required in the parser. */
> +  if (expr == NULL_TREE)

Then assert!


> +/* placeholder; in case we really need something more than the contextual
> +   checks.  */
Is this still needed?

> +static tree
> +check_co_return_expr (tree retval, bool *no_warning)

> +/* 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 (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_RETRN_EXPR, void_type_node, expr, NULL_TREE);
> +	  expr = maybe_cleanup_point_expr_void (expr);
> +	  expr = add_stmt (expr);
> +	  return expr;
> +	}
> +    }

finish_co_{await,yield,return} all look very similar, or at least start 
that way.  Is there an underlying helper fn waiting?


> +  /* 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_member (current_function_decl, "return_void", kw,
> +				 true /*musthave*/);
idiom is /*musthave=*/true


> +
> +  expr = build2_loc (kw, CO_RETRN_EXPR, void_type_node, expr, co_ret_call);
oh, please name it CO_RETURN_EXPR

> +/* ================= Morph and Expand. =================

I'll review more later ...

nathan
-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 4/6] Middle end expanders and transforms.
  2019-11-17 10:26       ` [C++ coroutines 4/6] Middle end expanders " Iain Sandoe
  2019-11-17 10:27         ` [C++ coroutines 5/6] Standard library header Iain Sandoe
@ 2019-11-19 10:00         ` Richard Biener
  2020-01-09 12:38           ` [C++ coroutines 4/7, v2] " Iain Sandoe
  1 sibling, 1 reply; 30+ messages in thread
From: Richard Biener @ 2019-11-19 10:00 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches, libstdc++

On Sun, Nov 17, 2019 at 11:27 AM Iain Sandoe <iain@sandoe.co.uk> wrote:
>
>
> As described in the covering note, the main part of this is the
> expansion of the library support builtins, these are simple boolean
> or numerical substitutions.
>
> The functionality of implementing an exit from scope without cleanup
> is performed here by lowering an IFN to a gimple goto.
>
> The final part is the expansion of the coroutine IFNs that describe the
> state machine connections to the dispatchers.
>
>    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.

Comments inline.

> gcc/ChangeLog:
>
> 2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
>
>         * Makefile.in: Add coroutine-passes.o.
>         * coroutine-passes.cc: New file.
>         * passes.def: Add pass_coroutine_lower_builtins,
>         pass_coroutine_early_expand_ifns and
>         pass_coroutine_finalize_frame.
>         * tree-pass.h (make_pass_coroutine_lower_builtins): New.
>         (make_pass_coroutine_early_expand_ifns): New.
>         (make_pass_coroutine_finalize_frame): New.
> ---
>  gcc/Makefile.in         |   1 +
>  gcc/coroutine-passes.cc | 621 ++++++++++++++++++++++++++++++++++++++++++++++++
>  gcc/passes.def          |   3 +
>  gcc/tree-pass.h         |   3 +
>  4 files changed, 628 insertions(+)
>  create mode 100644 gcc/coroutine-passes.cc
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index ac21401..fc7226a 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1266,6 +1266,7 @@ OBJS = \
>         compare-elim.o \
>         context.o \
>         convert.o \
> +       coroutine-passes.o \
>         coverage.o \
>         cppbuiltin.o \
>         cppdefault.o \
> diff --git a/gcc/coroutine-passes.cc b/gcc/coroutine-passes.cc
> new file mode 100644
> index 0000000..33e1d38
> --- /dev/null
> +++ b/gcc/coroutine-passes.cc
> @@ -0,0 +1,621 @@
> +/* coroutine expansion and optimisation passes.
> +
> +   Copyright (C) 2018-2019 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 jumpt 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, false /* don't re-do EH.  */);
> +      *handled_ops_p = true;
> +      return NULL_TREE;
> +    }
> +
> +  /* The remaining builtins implement the library interfaces to the coro
> +     frame.  */
> +  tree decl = gimple_call_fndecl (stmt);
> +  if (decl && fndecl_built_in_p (decl, BUILT_IN_NORMAL))
> +    {
> +      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_assert (TREE_CODE (align_t) == INTEGER_CST);
> +           gcc_assert (TREE_CODE (from) == INTEGER_CST);

Do  you actually verify and error for non-INTEGER_CSTs in the
frontend?  Users can
inject malicous calls to builtins at their discretion.

> +           bool dir = wi::to_wide (from) != 0;
> +           tree vptr = build_pointer_type (void_type_node);

this is ptr_type_node (also elsewhere)

> +           HOST_WIDE_INT promise_align = TREE_INT_CST_LOW (align_t);
> +           HOST_WIDE_INT psize = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (vptr));
> +           HOST_WIDE_INT align = TYPE_ALIGN_UNIT (vptr);
> +           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, vptr, ptr,
> +                               build_int_cst (sizetype, offs));

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.  */
> +           tree vptr = build_pointer_type (void_type_node);
> +           HOST_WIDE_INT psize = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (vptr));
> +           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,
> +                                        wide_int_to_tree (fntype_ppp, offset));

You are reading a pointer to the coro_resume builtin here?  You are careful
with using pointer-to-function types but the offset calculation uses the size
of void * here.  Note that the type of the offset operand in a MEM_REF denotes
TBAA info, so stores to whatever is at *ptr should better have matching types
(in this light re-using the function type of the builtin might be iffy).

While wide_int_to_tree seems to match HOST_WIDE_INT the canonical
way across the code-base is to use build_int_cst (type, hwint).

> +           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 vptr = build_pointer_type (void_type_node);
> +           tree vpp = build_pointer_type (vptr);
> +           tree indirect
> +             = fold_build2 (MEM_REF, vpp, ptr, wide_int_to_tree (vpp, 0));

Likewise for the comment about TBAA.

> +           tree d_ptr_tmp = make_ssa_name (TYPE_MAIN_VARIANT (vptr));

No need for TYPE_MAIN_VARIANT here if you use 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,
> +                                    wide_int_to_tree (vptr, 0));

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);

it would be nice to elide the function walk for functions not
containing any CO* stuff (you noted that below for other parts).
We can spend a bit in struct function noting functions with
coroutine code inside and set the bit from frontends or from
the gimplifier for example.  Since it's behind the flag_coroutines
paywall this can be addressed as followup.

> +
> +  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 renaming 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;
> +
> +  /* Some of the possible YIELD points will hopefully have been removed by
> +     earlier optimisations, record the ones that are present.  */
> +  hash_map<int_hash<HOST_WIDE_INT, -1, -2>, tree> destinations;
> +  hash_set<tree> to_remove;
> +  bool changed = false;
> +
> +  basic_block bb;

A comment on why we need two IL walks would be nice.  It looks like you
could have remembered CO_ACTOR calls in the first walk in a vec<gcall *>
for example and then just process that?

> +  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_ACTOR:
> +           changed = true;
> +           gsi_next (&gsi);
> +           break;
> +         case IFN_CO_YIELD:
> +           {
> +             /* .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);
> +                 debug_gimple_stmt (stmt);
> +               }
> +             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);
> +                 debug_gimple_stmt (stmt);
> +               }
> +             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
> +               debug_gimple_stmt (stmt);
> +             changed = true;
> +             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;
> +    }
> +
> +  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;
> +         }
> +       if (gimple_call_internal_fn (stmt) != IFN_CO_ACTOR)
> +         gsi_next (&gsi);
> +       else
> +         {
> +           if (dump_file)
> +             fprintf (dump_file, "saw CO_ACTOR in BB %u\n", bb->index);
> +           /* get yield point.  */
> +           HOST_WIDE_INT idx = TREE_INT_CST_LOW (gimple_call_arg (stmt, 0));
> +           tree *seen = destinations.get (idx);
> +           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);
> +               changed = true;
> +             }
> +           /* The remove advances the iterator.  */
> +           if (gsi_end_p (gsi))
> +             break;
> +           continue;
> +         }
> +      }
> +
> +  if (changed)
> +    {
> +      /* 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)

An extra walk over all BBs looks a bit excessive?  The label_to_block
map should be set up so you could walk the set and from there
affected blocks.

> +       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.  */
> +                 }
> +             }
> +           gsi_next (&gsi);

At least you can stop walking a BB if you hit something else
than a label.  Thus, else break;

> +         }
> +
> +      /* 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 *) { return flag_coroutines; };
> +
> +  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);
> +}
> +
> +/* Optimize (not yet) and lower frame allocation.
> +
> +   This is a place-holder for an optimisation to remove unused frame
> +   entries and re-size the frame to minimum.  */

I wonder if this will ever be reasonably possible at this point of the
compilation.  So instead of having this placeholder please do
the copy through during one of the lowering phases.

> +static unsigned int
> +execute_finalize_frame (void)
> +{
> +  /* Don't rebuild stuff unless we have to. */
> +  unsigned int todoflags = 0;
> +  bool changed = false;
> +
> +  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:
> +           {
> +             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);
> +             changed = true;
> +           }
> +           break;
> +         default:
> +           gsi_next (&gsi);
> +           break;
> +         }
> +      }
> +
> +  if (!changed)
> +    {
> +      if (dump_file)
> +       fprintf (dump_file, "coro: nothing to do\n");
> +      return todoflags;
> +    }
> +  else if (dump_file)
> +    fprintf (dump_file, "called frame expansion for %s\n",
> +            IDENTIFIER_POINTER (DECL_NAME (current_function_decl)));
> +  return 0;
> +}
> +
> +namespace {
> +
> +const pass_data pass_data_coroutine_finalize_frame = {
> +  GIMPLE_PASS,          /* type */
> +  "coro-finalize-frame", /* name */
> +  OPTGROUP_NONE,        /* optinfo_flags */
> +  TV_NONE,              /* tv_id */
> +  (PROP_cfg | PROP_ssa), /* properties_required */
> +  0,                    /* properties_provided */
> +  0,                    /* properties_destroyed */
> +  0,                    /* todo_flags_start */
> +  0                     /* todo_flags_finish, set this in the fn. */
> +};
> +
> +class pass_coroutine_finalize_frame : public gimple_opt_pass
> +{
> +public:
> +  pass_coroutine_finalize_frame (gcc::context *ctxt)
> +    : gimple_opt_pass (pass_data_coroutine_finalize_frame, ctxt)
> +  {}
> +
> +  /* opt_pass methods:
> +     FIXME: we should not execute this for every function, even when
> +     coroutines are enabled, it should be only for the ramp - or any
> +     function into which the ramp is inlined.  */
> +  virtual bool gate (function *) { return flag_coroutines; };
> +
> +  virtual unsigned int execute (function *f ATTRIBUTE_UNUSED)
> +  {
> +    return execute_finalize_frame ();
> +  }
> +
> +}; // class pass_coroutine_finalize_frame
> +
> +} // namespace
> +
> +gimple_opt_pass *
> +make_pass_coroutine_finalize_frame (gcc::context *ctxt)
> +{
> +  return new pass_coroutine_finalize_frame (ctxt);
> +}
> diff --git a/gcc/passes.def b/gcc/passes.def
> index 798a391..4353364 100644
> --- a/gcc/passes.def
> +++ b/gcc/passes.def
> @@ -39,8 +39,10 @@ along with GCC; see the file COPYING3.  If not see
>    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);

So the two passes above are really close - what's the reason to not
do coroutine_lower_builtins when the CFG is already built?

Otherwise looks OK.

Thanks,
Richard.

>    NEXT_PASS (pass_expand_omp);
>    NEXT_PASS (pass_warn_printf);
>    NEXT_PASS (pass_walloca, /*strict_mode_p=*/true);
> @@ -394,6 +396,7 @@ along with GCC; see the file COPYING3.  If not see
>    NEXT_PASS (pass_cleanup_eh);
>    NEXT_PASS (pass_lower_resx);
>    NEXT_PASS (pass_nrv);
> +  NEXT_PASS (pass_coroutine_finalize_frame);
>    NEXT_PASS (pass_cleanup_cfg_post_optimizing);
>    NEXT_PASS (pass_warn_function_noreturn);
>    NEXT_PASS (pass_gen_hsail);
> diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
> index a987661..e14abb1 100644
> --- a/gcc/tree-pass.h
> +++ b/gcc/tree-pass.h
> @@ -478,6 +478,9 @@ extern gimple_opt_pass *make_pass_gen_hsail (gcc::context *ctxt);
>  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);
> +extern gimple_opt_pass *make_pass_coroutine_finalize_frame (gcc::context *ctxt);
>
>  /* IPA Passes */
>  extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);
> --
> 2.8.1
>
>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 6/6] Testsuite.
  2019-11-17 10:28           ` [C++ coroutines 6/6] Testsuite Iain Sandoe
@ 2019-11-19 10:01             ` Richard Biener
  2020-01-09 12:40               ` [C++ coroutines 6/7, v2] Testsuite Iain Sandoe
  2019-11-20 11:13             ` [C++ coroutines 6/6] Testsuite JunMa
  1 sibling, 1 reply; 30+ messages in thread
From: Richard Biener @ 2019-11-19 10:01 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches, libstdc++

On Sun, Nov 17, 2019 at 11:29 AM Iain Sandoe <iain@sandoe.co.uk> wrote:
>
>
> There are two categories of test:
>
> 1. Checks for correctly formed source code and the error reporting.
> 2. Checks for transformation and code-gen.
>
> The second set are run as 'torture' tests for the standard options
> set, including LTO.  These are also intentionally run with no options
> provided (from the coroutines.exp script).

OK once the rest is approved.

Richard.

> gcc/testsuite/ChangeLog:
>
> 2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
>
>         * g++.dg/coroutines/co-yield-syntax-1.C: New test.
>         * g++.dg/coroutines/co-yield-syntax-2.C: New test.
>         * g++.dg/coroutines/co-yield-syntax-3.C: New test.
>         * g++.dg/coroutines/coro-auto-fn.C: New test.
>         * g++.dg/coroutines/coro-await-context-auto-fn.C: New test.
>         * g++.dg/coroutines/coro-bad-return.C: New test.
>         * g++.dg/coroutines/coro-builtins.C: New test.
>         * g++.dg/coroutines/coro-constexpr-fn.C: New test.
>         * g++.dg/coroutines/coro-context-ctor-dtor.C: New test.
>         * g++.dg/coroutines/coro-context-main.C: New test.
>         * g++.dg/coroutines/coro-context-vararg.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 test.
>         * g++.dg/coroutines/coroutines.exp: New file.
>         * g++.dg/coroutines/torture/co-await-0-triv.C: New test.
>         * g++.dg/coroutines/torture/co-await-1-value.C: New test.
>         * g++.dg/coroutines/torture/co-await-2-xform.C: New test.
>         * g++.dg/coroutines/torture/co-await-3-rhs-op.C: New test.
>         * g++.dg/coroutines/torture/co-await-4-control-flow.C: New test.
>         * g++.dg/coroutines/torture/co-await-5-loop.C: New test.
>         * g++.dg/coroutines/torture/co-await-6-ovl.C: New test.
>         * g++.dg/coroutines/torture/co-await-7-tmpl.C: New test.
>         * g++.dg/coroutines/torture/co-await-8-cascade.C: New test.
>         * g++.dg/coroutines/torture/co-await-9-pair.C: New test.
>         * g++.dg/coroutines/torture/co-ret-3.C: New test.
>         * g++.dg/coroutines/torture/co-ret-4.C: New test.
>         * g++.dg/coroutines/torture/co-ret-5.C: New test.
>         * g++.dg/coroutines/torture/co-ret-6.C: New test.
>         * g++.dg/coroutines/torture/co-ret-7.C: New test.
>         * g++.dg/coroutines/torture/co-ret-8.C: New test.
>         * g++.dg/coroutines/torture/co-ret-9.C: New test.
>         * g++.dg/coroutines/torture/co-ret-void-is-ready.C: New test.
>         * g++.dg/coroutines/torture/co-ret-void-is-suspend.C: New test.
>         * g++.dg/coroutines/torture/co-yield-0-triv.C: New test.
>         * g++.dg/coroutines/torture/co-yield-1-multi.C: New test.
>         * g++.dg/coroutines/torture/co-yield-2-loop.C: New test.
>         * g++.dg/coroutines/torture/co-yield-3-tmpl.C: New test.
>         * g++.dg/coroutines/torture/co-yield-strings.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-0.C: New test.
>         * g++.dg/coroutines/torture/func-params-1.C: New test.
>         * g++.dg/coroutines/torture/func-params-2.C: New test.
>         * g++.dg/coroutines/torture/func-params-3.C: New test.
>         * g++.dg/coroutines/torture/func-params-4.C: New test.
>         * g++.dg/coroutines/torture/func-params-5.C: New test.
>         * g++.dg/coroutines/torture/gro_on_alloc_fail_0.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/co-yield-syntax-1.C          |   6 +
>  .../g++.dg/coroutines/co-yield-syntax-2.C          |   6 +
>  .../g++.dg/coroutines/co-yield-syntax-3.C          |  37 ++++
>  gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C     |  12 ++
>  .../g++.dg/coroutines/coro-await-context-auto-fn.C |  16 ++
>  gcc/testsuite/g++.dg/coroutines/coro-bad-return.C  |  48 ++++++
>  gcc/testsuite/g++.dg/coroutines/coro-builtins.C    |  17 ++
>  .../g++.dg/coroutines/coro-constexpr-fn.C          |  12 ++
>  .../g++.dg/coroutines/coro-context-ctor-dtor.C     |   8 +
>  .../g++.dg/coroutines/coro-context-main.C          |   7 +
>  .../g++.dg/coroutines/coro-context-vararg.C        |  13 ++
>  gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C |  32 ++++
>  .../g++.dg/coroutines/coro-missing-promise-yield.C |  35 ++++
>  .../g++.dg/coroutines/coro-missing-ret-value.C     |  34 ++++
>  .../g++.dg/coroutines/coro-missing-ret-void.C      |  34 ++++
>  .../g++.dg/coroutines/coro-missing-ueh-1.C         |  14 ++
>  .../g++.dg/coroutines/coro-missing-ueh-2.C         |  16 ++
>  .../g++.dg/coroutines/coro-missing-ueh-3.C         |  17 ++
>  gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h |  25 +++
>  gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C    |   9 +
>  gcc/testsuite/g++.dg/coroutines/coro.h             | 123 ++++++++++++++
>  gcc/testsuite/g++.dg/coroutines/coroutines.exp     |  50 ++++++
>  .../g++.dg/coroutines/torture/co-await-0-triv.C    | 144 ++++++++++++++++
>  .../g++.dg/coroutines/torture/co-await-1-value.C   | 149 ++++++++++++++++
>  .../g++.dg/coroutines/torture/co-await-2-xform.C   | 155 +++++++++++++++++
>  .../g++.dg/coroutines/torture/co-await-3-rhs-op.C  | 155 +++++++++++++++++
>  .../coroutines/torture/co-await-4-control-flow.C   | 148 ++++++++++++++++
>  .../g++.dg/coroutines/torture/co-await-5-loop.C    | 147 ++++++++++++++++
>  .../g++.dg/coroutines/torture/co-await-6-ovl.C     | 153 +++++++++++++++++
>  .../g++.dg/coroutines/torture/co-await-7-tmpl.C    | 149 ++++++++++++++++
>  .../g++.dg/coroutines/torture/co-await-8-cascade.C | 157 +++++++++++++++++
>  .../g++.dg/coroutines/torture/co-await-9-pair.C    | 155 +++++++++++++++++
>  gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C | 112 ++++++++++++
>  gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C | 129 ++++++++++++++
>  gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C | 122 +++++++++++++
>  gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C | 125 ++++++++++++++
>  gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C | 114 +++++++++++++
>  gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C | 126 ++++++++++++++
>  gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C | 118 +++++++++++++
>  .../coroutines/torture/co-ret-void-is-ready.C      | 107 ++++++++++++
>  .../coroutines/torture/co-ret-void-is-suspend.C    | 111 ++++++++++++
>  .../g++.dg/coroutines/torture/co-yield-0-triv.C    | 146 ++++++++++++++++
>  .../g++.dg/coroutines/torture/co-yield-1-multi.C   | 159 +++++++++++++++++
>  .../g++.dg/coroutines/torture/co-yield-2-loop.C    | 153 +++++++++++++++++
>  .../g++.dg/coroutines/torture/co-yield-3-tmpl.C    | 160 +++++++++++++++++
>  .../g++.dg/coroutines/torture/co-yield-strings.C   | 182 ++++++++++++++++++++
>  .../g++.dg/coroutines/torture/coro-torture.exp     |  19 +++
>  .../g++.dg/coroutines/torture/exceptions-test-0.C  | 189 +++++++++++++++++++++
>  .../g++.dg/coroutines/torture/func-params-0.C      | 126 ++++++++++++++
>  .../g++.dg/coroutines/torture/func-params-1.C      | 130 ++++++++++++++
>  .../g++.dg/coroutines/torture/func-params-2.C      | 134 +++++++++++++++
>  .../g++.dg/coroutines/torture/func-params-3.C      | 133 +++++++++++++++
>  .../g++.dg/coroutines/torture/func-params-4.C      | 141 +++++++++++++++
>  .../g++.dg/coroutines/torture/func-params-5.C      | 141 +++++++++++++++
>  .../coroutines/torture/gro_on_alloc_fail_0.C       | 137 +++++++++++++++
>  .../g++.dg/coroutines/torture/local-var-0.C        | 123 ++++++++++++++
>  .../g++.dg/coroutines/torture/local-var-1.C        | 123 ++++++++++++++
>  .../g++.dg/coroutines/torture/local-var-2.C        | 135 +++++++++++++++
>  .../g++.dg/coroutines/torture/local-var-3.C        | 153 +++++++++++++++++
>  .../g++.dg/coroutines/torture/local-var-4.C        | 162 ++++++++++++++++++
>  .../coroutines/torture/mid-suspend-destruction-0.C | 127 ++++++++++++++
>  61 files changed, 5920 insertions(+)
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-builtins.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-main.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro.h
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coroutines.exp
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
>
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
> new file mode 100644
> index 0000000..30db0e9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
> @@ -0,0 +1,6 @@
> +//  { 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} }
> +
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
> new file mode 100644
> index 0000000..71e119f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
> @@ -0,0 +1,6 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +
> +#include "coro.h"
> +
> +int a[] = { co_yield 21 }; // { dg-error {'co_yield' cannot be used outside a function} }
> +
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
> new file mode 100644
> index 0000000..20a5b56
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
> @@ -0,0 +1,37 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#include "coro.h"
> +
> +namespace coro = std::experimental;
> +
> +/* Diagose missing return_void() in the promise type.  */
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
> new file mode 100644
> index 0000000..93a04dc
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
> @@ -0,0 +1,12 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
> new file mode 100644
> index 0000000..7f4ed9a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
> @@ -0,0 +1,16 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C b/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
> new file mode 100644
> index 0000000..e5b848d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
> @@ -0,0 +1,48 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "coro.h"
> +#endif
> +namespace coro = std::experimental;
> +
> +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 "return statement not allowed" }
> +{
> +  if (x)
> +    return Coro();
> +  else
> +    co_return;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-builtins.C b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
> new file mode 100644
> index 0000000..d7c4883
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
> @@ -0,0 +1,17 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
> new file mode 100644
> index 0000000..69b109f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
> @@ -0,0 +1,12 @@
> +//  { 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 ();
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
> new file mode 100644
> index 0000000..9396432
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
> @@ -0,0 +1,8 @@
> +//  { 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" }
> +};
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-main.C b/gcc/testsuite/g++.dg/coroutines/coro-context-main.C
> new file mode 100644
> index 0000000..40d7e4e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-context-main.C
> @@ -0,0 +1,7 @@
> +//  { 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" }
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C b/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
> new file mode 100644
> index 0000000..55a0295
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
> @@ -0,0 +1,13 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
> new file mode 100644
> index 0000000..8bedb77
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
> @@ -0,0 +1,32 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#include "coro.h"
> +
> +namespace coro = std::experimental;
> +
> +/* Diagose missing return_void() in the promise type.  */
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
> new file mode 100644
> index 0000000..95f567f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
> @@ -0,0 +1,35 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#include "coro.h"
> +
> +namespace coro = std::experimental;
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
> new file mode 100644
> index 0000000..fd81ce7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
> @@ -0,0 +1,34 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#include "coro.h"
> +
> +namespace coro = std::experimental;
> +
> +/* Diagose missing return_void() in the promise type.  */
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
> new file mode 100644
> index 0000000..17a2180
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
> @@ -0,0 +1,34 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#include "coro.h"
> +
> +namespace coro = std::experimental;
> +
> +/* 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
> new file mode 100644
> index 0000000..964b9f1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
> @@ -0,0 +1,14 @@
> +//  { dg-additional-options "-fsyntax-only -fexceptions -w" }
> +#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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
> new file mode 100644
> index 0000000..caf8fb9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
> @@ -0,0 +1,16 @@
> +//  { dg-additional-options "-fsyntax-only -fno-exceptions " }
> +#include "coro.h"
> +#include "coro-missing-ueh.h"
> +
> +// The missing method is warned for when exceptions are off and pedantic
> +// is on (default in the testsuite).
> +MissingUEH
> +bar () // { dg-warning {no member named 'unhandled_exception' in} }
> +{
> +  co_return;
> +}
> +
> +int main (int ac, char *av[]) {
> +  MissingUEH x = bar ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
> new file mode 100644
> index 0000000..f6ca196
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
> @@ -0,0 +1,17 @@
> +//  { dg-additional-options "-fsyntax-only -fno-exceptions -Wno-pedantic" }
> +#include "coro.h"
> +#include "coro-missing-ueh.h"
> +
> +/* We don't warn about the missing method, unless in pedantic mode, so
> +   this compile should be clean.  */
> +
> +MissingUEH
> +bar ()
> +{
> +  co_return;
> +}
> +
> +int main (int ac, char *av[]) {
> +  MissingUEH x = bar ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
> new file mode 100644
> index 0000000..7a32354
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
> @@ -0,0 +1,25 @@
> +#ifndef __MissingUEH_H
> +#define __MissingUEH_H
> +
> +namespace coro = std::experimental;
> +
> +/* Diagose missing unhandled_exception() in the promise type.  */
> +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
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
> new file mode 100644
> index 0000000..f22a5e0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
> @@ -0,0 +1,9 @@
> +// 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro.h b/gcc/testsuite/g++.dg/coroutines/coro.h
> new file mode 100644
> index 0000000..64df497
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro.h
> @@ -0,0 +1,123 @@
> +#if __has_include(<experimental/coroutine>)
> +
> +#include <experimental/coroutine>
> +
> +#else
> +#ifndef __CORO_H_n4835
> +#define __CORO_H_n4835
> +
> +// Fragments (with short-cuts) to mimic enough of the library header to
> +// make some progress.
> +
> +#if __cpp_coroutines
> +
> +namespace std {
> +namespace experimental {
> +inline namespace coroutines_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);
> +    }
> +    bool suspended_p () const {
> +      return __builtin_coro_is_suspended (__fr_ptr);
> +    }
> +  protected:
> +    void *__fr_ptr;
> +
> +  private:
> +    bool __is_suspended() const noexcept  {
> +      return __builtin_coro_is_suspended (__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 std::experimental::coroutines_n4775
> +
> +#else
> +# error "coro.h requires support for coroutines, add -fcoroutines"
> +#endif
> +#endif // __CORO_H_n4835
> +
> +#endif
> diff --git a/gcc/testsuite/g++.dg/coroutines/coroutines.exp b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
> new file mode 100644
> index 0000000..0696cc8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
> @@ -0,0 +1,50 @@
> +#   Copyright (C) 2018-2019 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
> new file mode 100644
> index 0000000..000d083
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
> @@ -0,0 +1,144 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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) {
> +        puts("Created coro1 object from handle");
> +  }
> +  coro1 (const coro1 &) = delete; // no copying
> +  coro1 (coro1 &&s) : handle(s.handle) {
> +    s.handle = nullptr;
> +    puts("Moved coro1");
> +  }
> +  coro1 &operator = (coro1 &&s) {
> +    handle = s.handle;
> +    s.handle = nullptr;
> +    return *this;
> +  }
> +  ~coro1() {
> +    puts("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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) {}
> +    ~suspend_always_intprt() {}
> +    bool await_ready() const noexcept { return false; }
> +    void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
> +    int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
> +  };
> +
> +  struct promise_type {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* The simplest valued co_await we can do.  */
> +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]");
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
> new file mode 100644
> index 0000000..cdc7b92
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
> @@ -0,0 +1,149 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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) {
> +        puts("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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) {}
> +    ~suspend_always_intprt() {}
> +    bool await_ready() const noexcept { return false; }
> +    void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
> +    int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
> +  };
> +
> +  struct promise_type {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* The simplest valued co_await we can do.  */
> +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]");
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
> new file mode 100644
> index 0000000..3b1bb71
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
> @@ -0,0 +1,155 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* Valued with an await_transform.  */
> +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]");
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
> new file mode 100644
> index 0000000..1fb54f9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
> @@ -0,0 +1,155 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* Valued with an await_transform.  */
> +int gX = 1;
> +
> +coro1
> +f ()
> +{
> +  gX = co_await 11 + 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]");
> +  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 (15) */
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
> new file mode 100644
> index 0000000..594dfe5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
> @@ -0,0 +1,148 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
> new file mode 100644
> index 0000000..86223be
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
> @@ -0,0 +1,147 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* 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();
> +  if (y != 42)
> +    {
> +      PRINTF ("main: y is wrong : %d, should be 42\n", y);
> +      //abort ();
> +    }
> +  puts ("main: done");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
> new file mode 100644
> index 0000000..fc67246
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
> @@ -0,0 +1,153 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) {}
> +    ~suspend_always_intprt() {}
> +    bool await_ready() const noexcept { return false; }
> +    void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
> +    int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
> +  };
> +
> +  struct promise_type {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +
> +  struct empty {
> +    auto operator co_await() const noexcept {
> +      return suspend_always_prt{};
> +    }
> +  };
> +};
> +
> +
> +/* The simplest valued co_await we can do.  */
> +int gX = 1;
> +coro1::empty e{};
> +
> +coro1
> +f ()
> +{
> +  co_await (e); /* overload */
> +  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]");
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
> new file mode 100644
> index 0000000..032e35f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
> @@ -0,0 +1,149 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 an int.  */
> +  struct suspend_always_intprt {
> +    T x;
> +    suspend_always_intprt() : x(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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
> new file mode 100644
> index 0000000..f3504d0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
> @@ -0,0 +1,157 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 * x;}
> +  };
> +
> +  struct promise_type {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* Valued with an await_transform.  */
> +int gX = 1;
> +coro1 f ()
> +{
> +  /* the await transform takes an int, the await_resume squares it.
> +     so we get 11 ** 4, 14641.  */
> +  gX = co_await 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] - nested");
> +  f_coro.handle.resume();
> +  PRINT ("main: resuming [2] - 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
> new file mode 100644
> index 0000000..8713adf
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
> @@ -0,0 +1,155 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* 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] one side of add");
> +  f_coro.handle.resume();
> +  PRINT ("main: resuming [1] 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
> new file mode 100644
> index 0000000..f57c4e9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
> @@ -0,0 +1,112 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// GRO differs from the eventual return type.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
> new file mode 100644
> index 0000000..d85199e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
> @@ -0,0 +1,129 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// GRO differs from eventual return type and has non-trivial dtor.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
> new file mode 100644
> index 0000000..0eb0055
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
> @@ -0,0 +1,122 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
> new file mode 100644
> index 0000000..1aad1b1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
> @@ -0,0 +1,125 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning a T.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
> new file mode 100644
> index 0000000..0af0922
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
> @@ -0,0 +1,114 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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");}
> +  };
> +
> +  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 {
> +  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{};
> +    //return std::experimental::suspend_always{};
> +  }
> +  auto final_suspend () {
> +    PRINT ("get final_suspend (always)");
> +    return suspend_always_prt{};
> +  }
> +  void return_void () {
> +    PRINT ("return_void ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +__attribute__((__noinline__))
> +int foo (void) { PRINT ("called the int fn foo");  return 2; }
> +
> +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();
> +  PRINT ("main: after resume");
> +  if (!x.handle.done())
> +    {
> +      PRINT ("main: apparently not done...");
> +      abort ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
> new file mode 100644
> index 0000000..f79201c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
> @@ -0,0 +1,126 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test templated co-return.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#define BROKEN
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
> new file mode 100644
> index 0000000..b2dd776
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
> @@ -0,0 +1,118 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* boolean return from await_suspend ().  */
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +struct coro1
> +f () noexcept
> +{
> +  PRINT ("coro1: about to return");
> +  co_return;
> +}
> +
> +int main ()
> +{
> +  //__builtin_coro_promise ((void*)0, 16, true);
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
> new file mode 100644
> index 0000000..f5662d1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
> @@ -0,0 +1,107 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +struct coro1
> +f () noexcept
> +{
> +  PRINT ("coro1: about to return");
> +  co_return;
> +}
> +
> +int main ()
> +{
> +  //__builtin_coro_promise ((void*)0, 16, true);
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
> new file mode 100644
> index 0000000..bc2ab1e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
> @@ -0,0 +1,111 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
> new file mode 100644
> index 0000000..4d76a99
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
> @@ -0,0 +1,146 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
> new file mode 100644
> index 0000000..341af61
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
> @@ -0,0 +1,159 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
> new file mode 100644
> index 0000000..53851d9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
> @@ -0,0 +1,153 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +int gX ;
> +
> +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;
> +
> +  //co_return 0;
> +}
> +
> +int main ()
> +{
> +  PRINT ("main: create coro1");
> +  struct coro1 f_coro = f ();
> +  PRINT ("main: got coro1 - resuming (1)");
> +  if (f_coro.handle.done())
> +    abort();
> +  f_coro.handle.resume();
> +  PRINT ("main: after resume (1)");
> +  int y = f_coro.handle.promise().get_value();
> +
> +  PRINT ("main: gX OK -- looping");
> +  do {
> +    //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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
> new file mode 100644
> index 0000000..b1659e8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
> @@ -0,0 +1,160 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test co_yield in templated code.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
> new file mode 100644
> index 0000000..9c953ba
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
> @@ -0,0 +1,182 @@
> +//  { dg-do run }
> +
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +#include <vector>
> +#include <string>
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +
> +  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 ();
> +      //x.handle.resume();
> +    }
> +
> +  if (gX != 6174)
> +    {
> +      PRINT ("main: apparently we didn't run mycounter DTOR...");
> +      abort ();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
> new file mode 100644
> index 0000000..d2463b2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
> @@ -0,0 +1,19 @@
> +# 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
> new file mode 100644
> index 0000000..9601564
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
> @@ -0,0 +1,189 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +#include <exception>
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {
> +    PRINT ("unhandled_exception: caught one!");/*exit(1);*/
> +    gX = -1;
> +    // returning from here should end up in final_suspend.
> +    //std::terminate ();
> +    }
> +  };
> +};
> +
> +// 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
> new file mode 100644
> index 0000000..308f7ef
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
> @@ -0,0 +1,126 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test promise construction from function args list.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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(int x) : value (x) {  PRINTF ("Created Promise with %d\n", x); }
> +  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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 )
> +    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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
> new file mode 100644
> index 0000000..4408ae1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
> @@ -0,0 +1,130 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
> new file mode 100644
> index 0000000..2d97e76
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
> @@ -0,0 +1,134 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test that we correctly re-write multiple uses of a function param
> +// in the body.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
> new file mode 100644
> index 0000000..1242b13
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
> @@ -0,0 +1,133 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test that we can use a function param in a co_xxxx status.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
> new file mode 100644
> index 0000000..729aae6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
> @@ -0,0 +1,141 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test that we can manage a constructed param copy.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +// 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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
> new file mode 100644
> index 0000000..e0c3d2f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
> @@ -0,0 +1,141 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test that we can manage a constructed param copy.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +// 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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C b/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
> new file mode 100644
> index 0000000..40a3620
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
> @@ -0,0 +1,137 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// check the code-gen for the failed alloc return.
> +
> +#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
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
> new file mode 100644
> index 0000000..f78787b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
> @@ -0,0 +1,123 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
> new file mode 100644
> index 0000000..9dd7ab3
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
> @@ -0,0 +1,123 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
> new file mode 100644
> index 0000000..886c913
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
> @@ -0,0 +1,135 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
> new file mode 100644
> index 0000000..53b7e71
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
> @@ -0,0 +1,153 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test modifying a local var and yielding several instances of it.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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{};
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
> new file mode 100644
> index 0000000..5d4c0ff
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
> @@ -0,0 +1,162 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test modifying a local var and yielding several instances of it.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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{};
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
> new file mode 100644
> index 0000000..6e68688
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
> @@ -0,0 +1,127 @@
> +// { 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.
> +
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// GRO differs from the eventual return type.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 (! x.handle.suspended_p())
> +    {
> +      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;
> +}
> --
> 2.8.1
>
>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 3/6] Front end parsing and transforms.
  2019-11-17 10:26     ` [C++ coroutines 3/6] Front end parsing and transforms Iain Sandoe
  2019-11-17 10:26       ` [C++ coroutines 4/6] Middle end expanders " Iain Sandoe
  2019-11-18 13:23       ` [C++ coroutines 3/6] Front end parsing " Nathan Sidwell
@ 2019-11-19 18:40       ` Nathan Sidwell
  2020-01-09 12:37         ` [C++ coroutines 3/7, v2] " Iain Sandoe
  2 siblings, 1 reply; 30+ messages in thread
From: Nathan Sidwell @ 2019-11-19 18:40 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches, libstdc++

On 11/17/19 5:25 AM, Iain Sandoe wrote:

> +++ b/gcc/cp/coroutines.cc

> +/* ================= Morph and Expand. =================

> +/* Helpers for label creation.  */
> +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_ARTIFICIAL (lab) = 1;
> +  DECL_IGNORED_P (lab) = 1;

We can use true for such boolean flags now, your call.


> +/* We mark our named labels as used, because we want to keep them in place
> +   during development.  FIXME: Remove this before integration.  */

FIXME?

> +struct __proxy_replace
> +{
> +  tree from, to;
> +};

std::pair<tree,tree> ?


> +/* If this is a coreturn statement (or one wrapped in a cleanup) then
> +   return the list of statements to replace it.  */
> +static tree

space between comment and function -- here and elsewhere.

> +  /* p.return_void and p.return_value are probably void, but it's not
> +     clear if that's intended to be a guarantee.  CHECKME.  */

CHECKME?

> +      /* We might have a single co_return statement, in which case, we do
> +	 have to replace it with a list.  */

'do have to' reads weirdly -> 'have to' or 'do not have to' ?


> +static tree
> +co_await_expander (tree *stmt, int * /*do_subtree*/, void *d)
> +{

> +      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), get_identifier ("resume"), 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);

Do we have a way of forcing this to be a tail call?  Should comment 
and/or TODO:

> +      append_to_statement_list (resume, &body_list);
> +    }

// comments approaching ...

> +  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);

// danger zone over :)  [there are others scattered around though]


> +/* 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, struct __await_xform_data *xform)
> +{
> +  struct 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);

that looks implementory speak -- is it an ICE?


> +  /* FIXME: determine if it's better to walk the co_await several times with
> +     a quick test, or once with a more complex test.  */

Probably can simply be an 'i'm not sure, I went with ... ' comment?


> +  /* 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... ??? CHECKME, this might be overly cautious.  */
?

> +      tree block = BIND_EXPR_BLOCK (first);
> +      if (block) // For this to be missing is probably a bug.

gcc_assert?

> +	{
> +	  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 ();
> +
> +  /* FIXME: this is development marker, remove later.  */

FIXME


> +  tree return_void = NULL_TREE;
> +  tree rvm
> +    = lookup_promise_member (orig, "return_void", loc, false /*musthave*/);
   /*musthave=*/false  I think there are other similar cases


> +  /* 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.  */
now->Now

> +  /* Here deallocate the frame (if we allocated it), which we will have at
> +     present.  */

sentence is confusing.  reword?

> +/* 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. */

pity we don't have a generic helper already.

> +  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))

This seems funky.  why do we care about templatedness?

> +    {
> +      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
   how can we get here?
> +    an = ACONCAT ((pfx, IDENTIFIER_POINTER (nm), sep, append, (char *) 0));
>

> +static bool
> +register_await_info (tree await_expr, tree aw_type, tree aw_nam, tree susp_type,
> +		     tree susp_handle_nam)
> +{

> +  if (seen)
> +    {
> +      error_at (EXPR_LOCATION (await_expr), "duplicate info for %qE",
> +		await_expr);
ICE?

> +
> +/* 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);


> +  debug_tree (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)
> +{

> +	  else
> +	    /* This wouldn't be broken, and we assume no need to replace it
> +	       but (ISTM) unexpected.  */
> +	    fprintf (stderr, "target expr init var real?\n");
?

> +	}
> +      else
> +	{
> +	  debug_tree (parm);
?

> +	  if (existed)
> +	    {
> +	      fprintf (stderr, "duplicate lvar: ");
> +	      debug_tree (lvar);
> +	      gcc_checking_assert (!existed);
?

> +	  /* TODO: Figure out if we should build a local type that has any
> +	     excess alignment or size from the original decl.  */
?

> +bool
> +morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
> +{
> +  if (!orig || TREE_CODE (orig) != FUNCTION_DECL)
> +    return false;

assert?

> +  gcc_assert (orig == current_function_decl);

If these have to be the same, why not just use the latter?


> +  /* 2. Types we need to define or look up.  */
> +
> +  suspend_points = hash_map<tree, struct suspend_point_info>::create_ggc (11);

If this is ggc allocated, then shouldn't suspend_points be marked GTY? 
However, IIUC this is only live during the processing of this function, 
so no GC will occur.  But take care with the early returns below in that 
case.

> +  /* FIXME: this is development marker, remove later.  */
that time has come :)

> +      if (block) // missing block is probably an error.
assert?  This looks similar to earlier code -- helper routine?

> +  /* ==== start to build the final functions.

funky format

> +     We push_deferring_access_checks to avoid these routines being seen as
> +     nested by the middle end, we are doing the outlining here.  */
a  weird side-effect of access pushing, but whatever.
> +
> +  push_deferring_access_checks (dk_no_check);


It probably makes sense for someone more familiar with the expanders 
than me to take a second look at the expander chunk of the file.

nathan

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 6/6] Testsuite.
  2019-11-17 10:28           ` [C++ coroutines 6/6] Testsuite Iain Sandoe
  2019-11-19 10:01             ` Richard Biener
@ 2019-11-20 11:13             ` JunMa
  2019-11-20 11:22               ` Iain Sandoe
  1 sibling, 1 reply; 30+ messages in thread
From: JunMa @ 2019-11-20 11:13 UTC (permalink / raw)
  To: Iain Sandoe, GCC Patches, libstdc++

脭脷 2019/11/17 脧脗脦莽6:28, Iain Sandoe 脨麓碌脌:
> There are two categories of test:
>
> 1. Checks for correctly formed source code and the error reporting.
> 2. Checks for transformation and code-gen.
>
> The second set are run as 'torture' tests for the standard options
> set, including LTO.  These are also intentionally run with no options
> provided (from the coroutines.exp script).
>
> gcc/testsuite/ChangeLog:
>
> 2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
>
> 	* g++.dg/coroutines/co-yield-syntax-1.C: New test.
> 	* g++.dg/coroutines/co-yield-syntax-2.C: New test.
> 	* g++.dg/coroutines/co-yield-syntax-3.C: New test.
> 	* g++.dg/coroutines/coro-auto-fn.C: New test.
> 	* g++.dg/coroutines/coro-await-context-auto-fn.C: New test.
> 	* g++.dg/coroutines/coro-bad-return.C: New test.
> 	* g++.dg/coroutines/coro-builtins.C: New test.
> 	* g++.dg/coroutines/coro-constexpr-fn.C: New test.
> 	* g++.dg/coroutines/coro-context-ctor-dtor.C: New test.
> 	* g++.dg/coroutines/coro-context-main.C: New test.
> 	* g++.dg/coroutines/coro-context-vararg.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 test.
> 	* g++.dg/coroutines/coroutines.exp: New file.
> 	* g++.dg/coroutines/torture/co-await-0-triv.C: New test.
> 	* g++.dg/coroutines/torture/co-await-1-value.C: New test.
> 	* g++.dg/coroutines/torture/co-await-2-xform.C: New test.
> 	* g++.dg/coroutines/torture/co-await-3-rhs-op.C: New test.
> 	* g++.dg/coroutines/torture/co-await-4-control-flow.C: New test.
> 	* g++.dg/coroutines/torture/co-await-5-loop.C: New test.
> 	* g++.dg/coroutines/torture/co-await-6-ovl.C: New test.
> 	* g++.dg/coroutines/torture/co-await-7-tmpl.C: New test.
> 	* g++.dg/coroutines/torture/co-await-8-cascade.C: New test.
> 	* g++.dg/coroutines/torture/co-await-9-pair.C: New test.
> 	* g++.dg/coroutines/torture/co-ret-3.C: New test.
> 	* g++.dg/coroutines/torture/co-ret-4.C: New test.
> 	* g++.dg/coroutines/torture/co-ret-5.C: New test.
> 	* g++.dg/coroutines/torture/co-ret-6.C: New test.
> 	* g++.dg/coroutines/torture/co-ret-7.C: New test.
> 	* g++.dg/coroutines/torture/co-ret-8.C: New test.
> 	* g++.dg/coroutines/torture/co-ret-9.C: New test.
> 	* g++.dg/coroutines/torture/co-ret-void-is-ready.C: New test.
> 	* g++.dg/coroutines/torture/co-ret-void-is-suspend.C: New test.
> 	* g++.dg/coroutines/torture/co-yield-0-triv.C: New test.
> 	* g++.dg/coroutines/torture/co-yield-1-multi.C: New test.
> 	* g++.dg/coroutines/torture/co-yield-2-loop.C: New test.
> 	* g++.dg/coroutines/torture/co-yield-3-tmpl.C: New test.
> 	* g++.dg/coroutines/torture/co-yield-strings.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-0.C: New test.
> 	* g++.dg/coroutines/torture/func-params-1.C: New test.
> 	* g++.dg/coroutines/torture/func-params-2.C: New test.
> 	* g++.dg/coroutines/torture/func-params-3.C: New test.
> 	* g++.dg/coroutines/torture/func-params-4.C: New test.
> 	* g++.dg/coroutines/torture/func-params-5.C: New test.
> 	* g++.dg/coroutines/torture/gro_on_alloc_fail_0.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/co-yield-syntax-1.C          |   6 +
>   .../g++.dg/coroutines/co-yield-syntax-2.C          |   6 +
>   .../g++.dg/coroutines/co-yield-syntax-3.C          |  37 ++++
>   gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C     |  12 ++
>   .../g++.dg/coroutines/coro-await-context-auto-fn.C |  16 ++
>   gcc/testsuite/g++.dg/coroutines/coro-bad-return.C  |  48 ++++++
>   gcc/testsuite/g++.dg/coroutines/coro-builtins.C    |  17 ++
>   .../g++.dg/coroutines/coro-constexpr-fn.C          |  12 ++
>   .../g++.dg/coroutines/coro-context-ctor-dtor.C     |   8 +
>   .../g++.dg/coroutines/coro-context-main.C          |   7 +
>   .../g++.dg/coroutines/coro-context-vararg.C        |  13 ++
>   gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C |  32 ++++
>   .../g++.dg/coroutines/coro-missing-promise-yield.C |  35 ++++
>   .../g++.dg/coroutines/coro-missing-ret-value.C     |  34 ++++
>   .../g++.dg/coroutines/coro-missing-ret-void.C      |  34 ++++
>   .../g++.dg/coroutines/coro-missing-ueh-1.C         |  14 ++
>   .../g++.dg/coroutines/coro-missing-ueh-2.C         |  16 ++
>   .../g++.dg/coroutines/coro-missing-ueh-3.C         |  17 ++
>   gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h |  25 +++
>   gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C    |   9 +
>   gcc/testsuite/g++.dg/coroutines/coro.h             | 123 ++++++++++++++
>   gcc/testsuite/g++.dg/coroutines/coroutines.exp     |  50 ++++++
>   .../g++.dg/coroutines/torture/co-await-0-triv.C    | 144 ++++++++++++++++
>   .../g++.dg/coroutines/torture/co-await-1-value.C   | 149 ++++++++++++++++
>   .../g++.dg/coroutines/torture/co-await-2-xform.C   | 155 +++++++++++++++++
>   .../g++.dg/coroutines/torture/co-await-3-rhs-op.C  | 155 +++++++++++++++++
>   .../coroutines/torture/co-await-4-control-flow.C   | 148 ++++++++++++++++
>   .../g++.dg/coroutines/torture/co-await-5-loop.C    | 147 ++++++++++++++++
>   .../g++.dg/coroutines/torture/co-await-6-ovl.C     | 153 +++++++++++++++++
>   .../g++.dg/coroutines/torture/co-await-7-tmpl.C    | 149 ++++++++++++++++
>   .../g++.dg/coroutines/torture/co-await-8-cascade.C | 157 +++++++++++++++++
>   .../g++.dg/coroutines/torture/co-await-9-pair.C    | 155 +++++++++++++++++
>   gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C | 112 ++++++++++++
>   gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C | 129 ++++++++++++++
>   gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C | 122 +++++++++++++
>   gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C | 125 ++++++++++++++
>   gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C | 114 +++++++++++++
>   gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C | 126 ++++++++++++++
>   gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C | 118 +++++++++++++
>   .../coroutines/torture/co-ret-void-is-ready.C      | 107 ++++++++++++
>   .../coroutines/torture/co-ret-void-is-suspend.C    | 111 ++++++++++++
>   .../g++.dg/coroutines/torture/co-yield-0-triv.C    | 146 ++++++++++++++++
>   .../g++.dg/coroutines/torture/co-yield-1-multi.C   | 159 +++++++++++++++++
>   .../g++.dg/coroutines/torture/co-yield-2-loop.C    | 153 +++++++++++++++++
>   .../g++.dg/coroutines/torture/co-yield-3-tmpl.C    | 160 +++++++++++++++++
>   .../g++.dg/coroutines/torture/co-yield-strings.C   | 182 ++++++++++++++++++++
>   .../g++.dg/coroutines/torture/coro-torture.exp     |  19 +++
>   .../g++.dg/coroutines/torture/exceptions-test-0.C  | 189 +++++++++++++++++++++
>   .../g++.dg/coroutines/torture/func-params-0.C      | 126 ++++++++++++++
>   .../g++.dg/coroutines/torture/func-params-1.C      | 130 ++++++++++++++
>   .../g++.dg/coroutines/torture/func-params-2.C      | 134 +++++++++++++++
>   .../g++.dg/coroutines/torture/func-params-3.C      | 133 +++++++++++++++
>   .../g++.dg/coroutines/torture/func-params-4.C      | 141 +++++++++++++++
>   .../g++.dg/coroutines/torture/func-params-5.C      | 141 +++++++++++++++
>   .../coroutines/torture/gro_on_alloc_fail_0.C       | 137 +++++++++++++++
>   .../g++.dg/coroutines/torture/local-var-0.C        | 123 ++++++++++++++
>   .../g++.dg/coroutines/torture/local-var-1.C        | 123 ++++++++++++++
>   .../g++.dg/coroutines/torture/local-var-2.C        | 135 +++++++++++++++
>   .../g++.dg/coroutines/torture/local-var-3.C        | 153 +++++++++++++++++
>   .../g++.dg/coroutines/torture/local-var-4.C        | 162 ++++++++++++++++++
>   .../coroutines/torture/mid-suspend-destruction-0.C | 127 ++++++++++++++
>   61 files changed, 5920 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-builtins.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-main.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coro.h
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/coroutines.exp
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
>   create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
>
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
> new file mode 100644
> index 0000000..30db0e9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-1.C
> @@ -0,0 +1,6 @@
> +//  { 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} }
> +
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
> new file mode 100644
> index 0000000..71e119f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-2.C
> @@ -0,0 +1,6 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +
> +#include "coro.h"
> +
> +int a[] = { co_yield 21 }; // { dg-error {'co_yield' cannot be used outside a function} }
> +
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
> new file mode 100644
> index 0000000..20a5b56
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-3.C
> @@ -0,0 +1,37 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#include "coro.h"
> +
> +namespace coro = std::experimental;
> +
> +/* Diagose missing return_void() in the promise type.  */
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
> new file mode 100644
> index 0000000..93a04dc
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-auto-fn.C
> @@ -0,0 +1,12 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
> new file mode 100644
> index 0000000..7f4ed9a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-await-context-auto-fn.C
> @@ -0,0 +1,16 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C b/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
> new file mode 100644
> index 0000000..e5b848d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-bad-return.C
> @@ -0,0 +1,48 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "coro.h"
> +#endif
> +namespace coro = std::experimental;
> +
> +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 "return statement not allowed" }
> +{
> +  if (x)
> +    return Coro();
> +  else
> +    co_return;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-builtins.C b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
> new file mode 100644
> index 0000000..d7c4883
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
> @@ -0,0 +1,17 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C b/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
> new file mode 100644
> index 0000000..69b109f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-constexpr-fn.C
> @@ -0,0 +1,12 @@
> +//  { 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 ();
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
> new file mode 100644
> index 0000000..9396432
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-context-ctor-dtor.C
> @@ -0,0 +1,8 @@
> +//  { 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" }
> +};
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-main.C b/gcc/testsuite/g++.dg/coroutines/coro-context-main.C
> new file mode 100644
> index 0000000..40d7e4e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-context-main.C
> @@ -0,0 +1,7 @@
> +//  { 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" }
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C b/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
> new file mode 100644
> index 0000000..55a0295
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-context-vararg.C
> @@ -0,0 +1,13 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
> new file mode 100644
> index 0000000..8bedb77
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
> @@ -0,0 +1,32 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#include "coro.h"
> +
> +namespace coro = std::experimental;
> +
> +/* Diagose missing return_void() in the promise type.  */
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
> new file mode 100644
> index 0000000..95f567f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
> @@ -0,0 +1,35 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#include "coro.h"
> +
> +namespace coro = std::experimental;
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
> new file mode 100644
> index 0000000..fd81ce7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
> @@ -0,0 +1,34 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#include "coro.h"
> +
> +namespace coro = std::experimental;
> +
> +/* Diagose missing return_void() in the promise type.  */
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
> new file mode 100644
> index 0000000..17a2180
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
> @@ -0,0 +1,34 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +#include "coro.h"
> +
> +namespace coro = std::experimental;
> +
> +/* 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
> new file mode 100644
> index 0000000..964b9f1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
> @@ -0,0 +1,14 @@
> +//  { dg-additional-options "-fsyntax-only -fexceptions -w" }
> +#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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
> new file mode 100644
> index 0000000..caf8fb9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
> @@ -0,0 +1,16 @@
> +//  { dg-additional-options "-fsyntax-only -fno-exceptions " }
> +#include "coro.h"
> +#include "coro-missing-ueh.h"
> +
> +// The missing method is warned for when exceptions are off and pedantic
> +// is on (default in the testsuite).
> +MissingUEH
> +bar () // { dg-warning {no member named 'unhandled_exception' in} }
> +{
> +  co_return;
> +}
> +
> +int main (int ac, char *av[]) {
> +  MissingUEH x = bar ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
> new file mode 100644
> index 0000000..f6ca196
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
> @@ -0,0 +1,17 @@
> +//  { dg-additional-options "-fsyntax-only -fno-exceptions -Wno-pedantic" }
> +#include "coro.h"
> +#include "coro-missing-ueh.h"
> +
> +/* We don't warn about the missing method, unless in pedantic mode, so
> +   this compile should be clean.  */
> +
> +MissingUEH
> +bar ()
> +{
> +  co_return;
> +}
> +
> +int main (int ac, char *av[]) {
> +  MissingUEH x = bar ();
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
> new file mode 100644
> index 0000000..7a32354
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
> @@ -0,0 +1,25 @@
> +#ifndef __MissingUEH_H
> +#define __MissingUEH_H
> +
> +namespace coro = std::experimental;
> +
> +/* Diagose missing unhandled_exception() in the promise type.  */
> +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
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
> new file mode 100644
> index 0000000..f22a5e0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
> @@ -0,0 +1,9 @@
> +// 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro.h b/gcc/testsuite/g++.dg/coroutines/coro.h
> new file mode 100644
> index 0000000..64df497
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro.h
> @@ -0,0 +1,123 @@
> +#if __has_include(<experimental/coroutine>)
> +
> +#include <experimental/coroutine>
> +
> +#else
> +#ifndef __CORO_H_n4835
> +#define __CORO_H_n4835
> +
> +// Fragments (with short-cuts) to mimic enough of the library header to
> +// make some progress.
> +
> +#if __cpp_coroutines
> +
> +namespace std {
> +namespace experimental {
> +inline namespace coroutines_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);
> +    }
> +    bool suspended_p () const {
> +      return __builtin_coro_is_suspended (__fr_ptr);
> +    }
> +  protected:
> +    void *__fr_ptr;
> +
> +  private:
> +    bool __is_suspended() const noexcept  {
> +      return __builtin_coro_is_suspended (__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 std::experimental::coroutines_n4775
> +
> +#else
> +# error "coro.h requires support for coroutines, add -fcoroutines"
> +#endif
> +#endif // __CORO_H_n4835
> +
> +#endif
> diff --git a/gcc/testsuite/g++.dg/coroutines/coroutines.exp b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
> new file mode 100644
> index 0000000..0696cc8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
> @@ -0,0 +1,50 @@
> +#   Copyright (C) 2018-2019 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
> new file mode 100644
> index 0000000..000d083
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-0-triv.C
> @@ -0,0 +1,144 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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) {
> +        puts("Created coro1 object from handle");
> +  }
> +  coro1 (const coro1 &) = delete; // no copying
> +  coro1 (coro1 &&s) : handle(s.handle) {
> +    s.handle = nullptr;
> +    puts("Moved coro1");
> +  }
> +  coro1 &operator = (coro1 &&s) {
> +    handle = s.handle;
> +    s.handle = nullptr;
> +    return *this;
> +  }
> +  ~coro1() {
> +    puts("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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) {}
> +    ~suspend_always_intprt() {}
> +    bool await_ready() const noexcept { return false; }
> +    void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
> +    int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
> +  };
> +
> +  struct promise_type {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* The simplest valued co_await we can do.  */
> +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]");
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
> new file mode 100644
> index 0000000..cdc7b92
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-1-value.C
> @@ -0,0 +1,149 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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) {
> +        puts("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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) {}
> +    ~suspend_always_intprt() {}
> +    bool await_ready() const noexcept { return false; }
> +    void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
> +    int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
> +  };
> +
> +  struct promise_type {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* The simplest valued co_await we can do.  */
> +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]");
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
> new file mode 100644
> index 0000000..3b1bb71
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-2-xform.C
> @@ -0,0 +1,155 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* Valued with an await_transform.  */
> +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]");
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
> new file mode 100644
> index 0000000..1fb54f9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-3-rhs-op.C
> @@ -0,0 +1,155 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* Valued with an await_transform.  */
> +int gX = 1;
> +
> +coro1
> +f ()
> +{
> +  gX = co_await 11 + 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]");
> +  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 (15) */
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
> new file mode 100644
> index 0000000..594dfe5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-4-control-flow.C
> @@ -0,0 +1,148 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
> new file mode 100644
> index 0000000..86223be
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-5-loop.C
> @@ -0,0 +1,147 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* 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();
> +  if (y != 42)
> +    {
> +      PRINTF ("main: y is wrong : %d, should be 42\n", y);
> +      //abort ();
> +    }
> +  puts ("main: done");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
> new file mode 100644
> index 0000000..fc67246
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-6-ovl.C
> @@ -0,0 +1,153 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) {}
> +    ~suspend_always_intprt() {}
> +    bool await_ready() const noexcept { return false; }
> +    void await_suspend(coro::coroutine_handle<>) const noexcept { puts ("susp-always-susp-int");}
> +    int await_resume() const noexcept {puts ("susp-always-resume-int"); return x;}
> +  };
> +
> +  struct promise_type {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +
> +  struct empty {
> +    auto operator co_await() const noexcept {
> +      return suspend_always_prt{};
> +    }
> +  };
> +};
> +
> +
> +/* The simplest valued co_await we can do.  */
> +int gX = 1;
> +coro1::empty e{};
> +
> +coro1
> +f ()
> +{
> +  co_await (e); /* overload */
> +  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]");
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
> new file mode 100644
> index 0000000..032e35f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-7-tmpl.C
> @@ -0,0 +1,149 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 an int.  */
> +  struct suspend_always_intprt {
> +    T x;
> +    suspend_always_intprt() : x(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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
> new file mode 100644
> index 0000000..f3504d0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-8-cascade.C
> @@ -0,0 +1,157 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 * x;}
> +  };
> +
> +  struct promise_type {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* Valued with an await_transform.  */
> +int gX = 1;
> +coro1 f ()
> +{
> +  /* the await transform takes an int, the await_resume squares it.
> +     so we get 11 ** 4, 14641.  */
> +  gX = co_await 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] - nested");
> +  f_coro.handle.resume();
> +  PRINT ("main: resuming [2] - 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
> new file mode 100644
> index 0000000..8713adf
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-9-pair.C
> @@ -0,0 +1,155 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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 {
> +    int 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 an int.  */
> +  struct suspend_always_intprt {
> +    int x;
> +    suspend_always_intprt() : x(5) { PRINT ("suspend_always_intprt def ctor"); }
> +    suspend_always_intprt(int _x) : x(_x) { PRINTF ("suspend_always_intprt ctor with %d\n", 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 {
> +  int 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 () %d\n",v);
> +    value = v;
> +  }
> +
> +  auto await_transform (int v) {
> +    PRINTF ("await_transform an int () %d\n",v);
> +    return suspend_always_intprt (v);
> +  }
> +
> +  int get_value () { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +/* 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] one side of add");
> +  f_coro.handle.resume();
> +  PRINT ("main: resuming [1] 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
> new file mode 100644
> index 0000000..f57c4e9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-3.C
> @@ -0,0 +1,112 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// GRO differs from the eventual return type.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
> new file mode 100644
> index 0000000..d85199e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-4.C
> @@ -0,0 +1,129 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// GRO differs from eventual return type and has non-trivial dtor.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
> new file mode 100644
> index 0000000..0eb0055
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-5.C
> @@ -0,0 +1,122 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
> new file mode 100644
> index 0000000..1aad1b1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-6.C
> @@ -0,0 +1,125 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning a T.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
> new file mode 100644
> index 0000000..0af0922
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-7.C
> @@ -0,0 +1,114 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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");}
> +  };
> +
> +  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 {
> +  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{};
> +    //return std::experimental::suspend_always{};
> +  }
> +  auto final_suspend () {
> +    PRINT ("get final_suspend (always)");
> +    return suspend_always_prt{};
> +  }
> +  void return_void () {
> +    PRINT ("return_void ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +__attribute__((__noinline__))
> +int foo (void) { PRINT ("called the int fn foo");  return 2; }
> +
> +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();
> +  PRINT ("main: after resume");
> +  if (!x.handle.done())
> +    {
> +      PRINT ("main: apparently not done...");
> +      abort ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
> new file mode 100644
> index 0000000..f79201c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-8.C
> @@ -0,0 +1,126 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test templated co-return.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#define BROKEN
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
> new file mode 100644
> index 0000000..b2dd776
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-9.C
> @@ -0,0 +1,118 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* boolean return from await_suspend ().  */
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +struct coro1
> +f () noexcept
> +{
> +  PRINT ("coro1: about to return");
> +  co_return;
> +}
> +
> +int main ()
> +{
> +  //__builtin_coro_promise ((void*)0, 16, true);
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
> new file mode 100644
> index 0000000..f5662d1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-ready.C
> @@ -0,0 +1,107 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +struct coro1
> +f () noexcept
> +{
> +  PRINT ("coro1: about to return");
> +  co_return;
> +}
> +
> +int main ()
> +{
> +  //__builtin_coro_promise ((void*)0, 16, true);
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
> new file mode 100644
> index 0000000..bc2ab1e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-void-is-suspend.C
> @@ -0,0 +1,111 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +  //int x;
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
> new file mode 100644
> index 0000000..4d76a99
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-0-triv.C
> @@ -0,0 +1,146 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
> new file mode 100644
> index 0000000..341af61
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-1-multi.C
> @@ -0,0 +1,159 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
> new file mode 100644
> index 0000000..53851d9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-2-loop.C
> @@ -0,0 +1,153 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +int gX ;
> +
> +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;
> +
> +  //co_return 0;
> +}
> +
> +int main ()
> +{
> +  PRINT ("main: create coro1");
> +  struct coro1 f_coro = f ();
> +  PRINT ("main: got coro1 - resuming (1)");
> +  if (f_coro.handle.done())
> +    abort();
> +  f_coro.handle.resume();
> +  PRINT ("main: after resume (1)");
> +  int y = f_coro.handle.promise().get_value();
> +
> +  PRINT ("main: gX OK -- looping");
> +  do {
> +    //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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
> new file mode 100644
> index 0000000..b1659e8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-3-tmpl.C
> @@ -0,0 +1,160 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test co_yield in templated code.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +
> +  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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
> new file mode 100644
> index 0000000..9c953ba
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-strings.C
> @@ -0,0 +1,182 @@
> +//  { dg-do run }
> +
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +#include <vector>
> +#include <string>
> +
> +namespace coro = std::experimental;
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +
> +  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 ();
> +      //x.handle.resume();
> +    }
> +
> +  if (gX != 6174)
> +    {
> +      PRINT ("main: apparently we didn't run mycounter DTOR...");
> +      abort ();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
> new file mode 100644
> index 0000000..d2463b2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
> @@ -0,0 +1,19 @@
> +# 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
> new file mode 100644
> index 0000000..9601564
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
> @@ -0,0 +1,189 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +#include <exception>
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {
> +    PRINT ("unhandled_exception: caught one!");/*exit(1);*/
> +    gX = -1;
> +    // returning from here should end up in final_suspend.
> +    //std::terminate ();
> +    }
> +  };
> +};
> +
> +// 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
> new file mode 100644
> index 0000000..308f7ef
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-0.C
> @@ -0,0 +1,126 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test promise construction from function args list.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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(int x) : value (x) {  PRINTF ("Created Promise with %d\n", x); }
> +  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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 )
> +    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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
> new file mode 100644
> index 0000000..4408ae1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-1.C
> @@ -0,0 +1,130 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
> new file mode 100644
> index 0000000..2d97e76
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-2.C
> @@ -0,0 +1,134 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test that we correctly re-write multiple uses of a function param
> +// in the body.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
> new file mode 100644
> index 0000000..1242b13
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-3.C
> @@ -0,0 +1,133 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test that we can use a function param in a co_xxxx status.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
> new file mode 100644
> index 0000000..729aae6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-4.C
> @@ -0,0 +1,141 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test that we can manage a constructed param copy.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +// 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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
> new file mode 100644
> index 0000000..e0c3d2f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-5.C
> @@ -0,0 +1,141 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test that we can manage a constructed param copy.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +// 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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C b/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
> new file mode 100644
> index 0000000..40a3620
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/gro_on_alloc_fail_0.C
> @@ -0,0 +1,137 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// check the code-gen for the failed alloc return.
> +
> +#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
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
> new file mode 100644
> index 0000000..f78787b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
> @@ -0,0 +1,123 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
> new file mode 100644
> index 0000000..9dd7ab3
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
> @@ -0,0 +1,123 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 ();
> +      //x.handle.resume();
> +    }
> +  PRINT ("main: returning");
> +  return 0;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
> new file mode 100644
> index 0000000..886c913
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
> @@ -0,0 +1,135 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test returning an int.
> +// We will use the promise to contain this to avoid having to include
> +// additional C++ headers.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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;
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
> new file mode 100644
> index 0000000..53b7e71
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
> @@ -0,0 +1,153 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test modifying a local var and yielding several instances of it.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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{};
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
> new file mode 100644
> index 0000000..5d4c0ff
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
> @@ -0,0 +1,162 @@
> +//  { dg-do run }
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// Test modifying a local var and yielding several instances of it.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> +
> +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{};
> +  }
> +  int get_value (void) { return value; }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
> new file mode 100644
> index 0000000..6e68688
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
> @@ -0,0 +1,127 @@
> +// { 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.
> +
> +#if __clang__
> +# include <experimental/coroutine>
> +# include <utility>
> +#else
> +# include "../coro.h"
> +#endif
> +
> +namespace coro = std::experimental;
> +
> +// GRO differs from the eventual return type.
> +
> +/* just to avoid cluttering dump files. */
> +extern "C" int puts (const char *);
> +extern "C" int printf (const char *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#else
> +#  define PRINT(X) puts(X)
> +#endif
> +
> +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 ()");
> +  }
> +  // Placeholder to satisfy parser, not doing exceptions yet.
> +  void unhandled_exception() {  /*exit(1);*/ }
> +  };
> +};
> +
> +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 (! x.handle.suspended_p())
> +    {
> +      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;
> +}

I find that the patches donot support 'for co_await'. And it is
quiet simple to implement range based 'for co_await' based on your
patches, since it's just need few more works on range for source to
source transform. Any reason for that?

Regards
JunMa

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 6/6] Testsuite.
  2019-11-20 11:13             ` [C++ coroutines 6/6] Testsuite JunMa
@ 2019-11-20 11:22               ` Iain Sandoe
  2019-11-20 13:11                 ` JunMa
  0 siblings, 1 reply; 30+ messages in thread
From: Iain Sandoe @ 2019-11-20 11:22 UTC (permalink / raw)
  To: JunMa; +Cc: GCC Patches, libstdc++

Hello JunMa,

JunMa <JunMa@linux.alibaba.com> wrote:

> 在 2019/11/17 下午6:28, Iain Sandoe 写道:

> I find that the patches donot support 'for co_await'. And it is
> quiet simple to implement range based 'for co_await' based on your
> patches, since it's just need few more works on range for source to
> source transform. Any reason for that?

If I understand your question correctly,

for example TS n4775, there was:

[stmt.stmt]
  ….
  for co_await ( for-range-declaration : for-range-initializer ) statement

yes?

This was removed by a later committee resolution, and was *not* merged
to the C++20 Working Draft (I am currently working to n4835).

So, the reason it is not implemented in GCC at present, is that it is not clear
exactly what form it might take if introduced in some future proposal for
enhanced coroutines.

hope that answers the question,
thanks
Iain

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 6/6] Testsuite.
  2019-11-20 11:22               ` Iain Sandoe
@ 2019-11-20 13:11                 ` JunMa
  0 siblings, 0 replies; 30+ messages in thread
From: JunMa @ 2019-11-20 13:11 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches, libstdc++

在 2019/11/20 下午7:22, Iain Sandoe 写道:
> Hello JunMa,
>
> JunMa <JunMa@linux.alibaba.com> wrote:
>
>> 在 2019/11/17 下午6:28, Iain Sandoe 写道:
>> I find that the patches donot support 'for co_await'. And it is
>> quiet simple to implement range based 'for co_await' based on your
>> patches, since it's just need few more works on range for source to
>> source transform. Any reason for that?
> If I understand your question correctly,
>
> for example TS n4775, there was:
>
> [stmt.stmt]
>    ….
>    for co_await ( for-range-declaration : for-range-initializer ) statement
>
> yes?
>
> This was removed by a later committee resolution, and was *not* merged
> to the C++20 Working Draft (I am currently working to n4835).
>
> So, the reason it is not implemented in GCC at present, is that it is not clear
> exactly what form it might take if introduced in some future proposal for
> enhanced coroutines.
>
> hope that answers the question,
> thanks
> Iain
Hi Iain,
     Thanks, that make sense.

Regards
JunMa

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 1/7] Common code and base definitions.
  2019-11-17 15:49   ` [C++ coroutines 1/6] Common code and base definitions Jeff Law
@ 2020-01-09 12:36     ` Iain Sandoe
  0 siblings, 0 replies; 30+ messages in thread
From: Iain Sandoe @ 2020-01-09 12:36 UTC (permalink / raw)
  To: GCC Patches; +Cc: libstdc++, Jeff Law

Jeff Law <law@redhat.com> wrote:

> On 11/17/19 3:24 AM, Iain Sandoe wrote:
>> This part of the patch series provides the gating flag, the keywords,
>> cpp defines etc.
>> 
>> gcc/ChangeLog:
>> 
>> 2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
>> 
>> 	* doc/invoke.texi: Document the fcoroutines command line
>> 	switch.
>> 
>> gcc/c-family/ChangeLog:
>> 
>> 2019-11-17  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.
>> 
>> gcc/cp/ChangeLog:
>> 
>> 2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
>> 
>> 	* cp-tree.h (lang_decl-fn): coroutine_p, new bit.
>> 	* lex.c (init_reswords): Enable keywords when the coroutine flag
>> 	is set,
>> 	* operators.def (co_await): New operator.
> Looks quite reasonable to me.  If you need minor twiddling due to
> reviewer feedback elsewhere those are pre-approved as well.

Here is the updated patch (this is actually unchanged apart from dates).
Iain

=====

This part of the patch series provides the gating flag, the keywords,
cpp defines etc.

gcc/ChangeLog:

2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>

	* doc/invoke.texi: Document the fcoroutines command line
	switch.

gcc/c-family/ChangeLog:

2020-01-09  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.

gcc/cp/ChangeLog:

2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>

	* 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.
---
 gcc/c-family/c-common.c     |  5 +++++
 gcc/c-family/c-common.h     |  5 +++++
 gcc/c-family/c-cppbuiltin.c |  2 ++
 gcc/c-family/c.opt          |  4 ++++
 gcc/cp/cp-tree.h            | 17 ++++++++++++++++-
 gcc/cp/lex.c                |  2 ++
 gcc/cp/operators.def        |  1 +
 gcc/doc/invoke.texi         |  4 ++++
 8 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 832d8a5dce..26a94a1535 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -537,6 +537,11 @@ const struct c_common_resword c_common_reswords[] =
   { "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 },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 37b0594995..59d4aaf443 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -189,6 +189,9 @@ enum rid
   /* 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,
 
@@ -433,9 +436,11 @@ extern machine_mode c_default_pointer_mode;
 #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[];
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index cb86941516..a6308921dc 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -1017,6 +1017,8 @@ c_cpp_builtins (cpp_reader *pfile)
           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.  */
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 230b76387b..aa0fa5deae 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1477,6 +1477,10 @@ fconstexpr-ops-limit=
 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.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 495ed6b9f5..553e447434 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2701,7 +2701,9 @@ struct GTY(()) lang_decl_fn {
   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.  */
 
@@ -4992,6 +4994,13 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #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) \
@@ -7931,6 +7940,12 @@ extern tree cp_ubsan_maybe_instrument_downcast	(location_t, tree, tree, tree);
 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 bool morph_fn_to_coro			(tree, tree *, tree *);
+
 /* Inline bodies.  */
 
 inline tree
diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c
index 11b872936a..37282d5697 100644
--- a/gcc/cp/lex.c
+++ b/gcc/cp/lex.c
@@ -233,6 +233,8 @@ init_reswords (void)
     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)
diff --git a/gcc/cp/operators.def b/gcc/cp/operators.def
index c0a659381b..d2395355af 100644
--- a/gcc/cp/operators.def
+++ b/gcc/cp/operators.def
@@ -87,6 +87,7 @@ DEF_OPERATOR ("++", PREINCREMENT_EXPR, "pp", OVL_OP_FLAG_UNARY)
 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)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 2b07c09def..abcab9515e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -2569,6 +2569,10 @@ of a loop too many expressions need to be evaluated, the resulting constexpr
 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
-- 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 2/7, v2] Define builtins and internal  functions.
  2019-11-17 16:54     ` [C++ coroutines 2/6] Define builtins and internal functions Jeff Law
@ 2020-01-09 12:36       ` Iain Sandoe
  0 siblings, 0 replies; 30+ messages in thread
From: Iain Sandoe @ 2020-01-09 12:36 UTC (permalink / raw)
  To: GCC Patches; +Cc: libstdc++, Jeff Law

Jeff Law <law@redhat.com> wrote:

> On 11/17/19 3:24 AM, Iain Sandoe wrote:
>> This part of the patch series provides the builtin functions
>> used by the standard library code and the internal functions
>> used to implement lowering of the coroutine state machine.
>> 
>> gcc/ChangeLog:
>> 
>> 2019-11-17  Iain Sandoe  <iain@sandoe.co.uk>
>> 
>> 	* builtin-types.def (BT_FN_BOOL_PTR): New.
>> 	(BT_FN_PTR_PTR_SIZE_BOOL): New.
>> 	* builtins.def (DEF_COROUTINE_BUILTIN): New.
>> 	* coroutine-builtins.def: New file.
>> 	* 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.
> This is OK as would be any minor adjustments you may ultimately need due
> to other feedback on the kit.

Said minor adjustments included in the patch below (the actual SVN commit IDs noted)
Iain

========

This part of the patch series provides the builtin functions
used by the standard library code and the internal functions
used to implement lowering of the coroutine state machine.

* Squashed commits:

r279756 - Const-qualify the align argument to promise builtin.
r279817 - Update copyright year.

gcc/ChangeLog:

2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>

	* 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.
	* 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.
---
 gcc/builtin-types.def      |  5 +++++
 gcc/builtins.def           |  9 ++++++++
 gcc/coroutine-builtins.def | 53 ++++++++++++++++++++++++++++++++++++++++++++++
 gcc/internal-fn.c          | 26 +++++++++++++++++++++++
 gcc/internal-fn.def        |  6 ++++++
 5 files changed, 99 insertions(+)
 create mode 100644 gcc/coroutine-builtins.def

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index b2a5a8be6c..c7aa691b24 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -131,6 +131,8 @@ DEF_PRIMITIVE_TYPE (BT_CONST_DOUBLE_PTR,
 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)
@@ -300,6 +302,7 @@ DEF_FUNCTION_TYPE_1 (BT_FN_UINT32_UINT32, BT_UINT32, BT_UINT32)
 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)
@@ -628,6 +631,8 @@ DEF_FUNCTION_TYPE_3 (BT_FN_VOID_UINT32_UINT32_PTR,
 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)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 674ca239ec..5ab842c34c 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -189,6 +189,12 @@ along with GCC; see the file COPYING3.  If not see
   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
@@ -1064,6 +1070,9 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE", BT_FN_INT, ATTR_NOTHROW_LEAF_LIST)
 /* 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.  */
diff --git a/gcc/coroutine-builtins.def b/gcc/coroutine-builtins.def
new file mode 100644
index 0000000000..3839cb54fa
--- /dev/null
+++ b/gcc/coroutine-builtins.def
@@ -0,0 +1,53 @@
+/* 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)
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index ceac2df9ce..52d1638917 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -2884,6 +2884,32 @@ expand_NOP (internal_fn, gcall *)
   /* 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.  */
 
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index ed11b93b67..1d190d492f 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -366,6 +366,12 @@ DEF_INTERNAL_FN (LAUNDER, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
 /* 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)
 
-- 
2.14.3

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 3/7, v2] Front end parsing and transforms.
  2019-11-19 18:40       ` Nathan Sidwell
@ 2020-01-09 12:37         ` Iain Sandoe
  0 siblings, 0 replies; 30+ messages in thread
From: Iain Sandoe @ 2020-01-09 12:37 UTC (permalink / raw)
  To: Nathan Sidwell, GCC Patches; +Cc: libstdc++

Hi Nathan,

Nathan Sidwell <nathan@acm.org> wrote:

> On 11/17/19 5:25 AM, Iain Sandoe wrote:
> 
>> +++ b/gcc/cp/coroutines.cc

I was able to address all the comments that you made without finding any
show-stoppers.  In addition to your comments, I’ve had one or two privately
(and likewise a few bug reports) these have been addressed in the revised
patch.

The SVN commit IDs  that address the changes specific to this patch set are
noted blow.

OK for trunk now?
Iain

=======


As described in the covering note, there are two parts to this.

1. Parsing, template instantiation and diagnostics for the standard-
   mandated class entries.

  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 transformatin until template expansion is
  complete so that we have complete types at that time.

2. AST analysis and transformation which performs the code-gen for the
   outlined state machine.

   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 analysed 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
     optimisations 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 instanceare 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.

* Squashed commits:
r278394 - Fix a typo preventing an early return from captures_temporary callback.
r278442 - Fix a code-gen error in co_return of non-void expr (we were evaluating the expression twice).
r278453 - NS comments (typographical)
r278464 - NS comments, rename CO_RETRN_EXPR => CO_RETURN_EXPR
r278480 - NS comments: Confirm that empty expressions to co_await/co_yield are
handled in the parser (add testcases to cover). Make the suspend point kind into an
enum and rename the variable. C++-ify the coroutine state struct.
r278649 - NS comments - use GTY for coroutine_info
r278650 - Check for en_US spelling and fix punctuation in error messages.
r278657 - NS comments - use global identifiers for traits,handle and promise
r278875 - NS more on identifiers, make em lazily init locals.
r279017 - remove more development code.
r279020 - Fix bug in handling co_return co_await.
r279079 - Fix a bug in traits lookup.
r279080 - Fix a bug in type dependency checks.
r279096 - Fix two build warnings.
r279097 - Address review comments, don't recompute some trees.
r279197 - Fix a bug in constexpr evaluation.
r279317 - Improve error recovery.
r279385 - Fix PR 92933 unnamed locals.
r279396 - Fix lambda capture of references.
r279397 - Identify captures in the frame.
r279404 - Fix a warning regression.
r279405 - Address review comments, more whitespace and comment changes.
r279462 - Fix a bug where await_resume methods return references.
r279544 - fix leak of a vect.
r279594 - Account co_await expressions captured by reference.
r279678 - Check this arg in CTORs and DTORs.
r279679 - Check for LAZY move CTORS/assigns.
r279727 - Remove an unused var.
r279757 - Add validation for promise builtin arguments.
r279817 - Update copyright year.
r279845 - move coroutine header to std.
r280004 - Add promise overloads of new and delete.
r280032 - Fix a build warning.

gcc/cp/ChangeLog:

2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>

	* Make-lang.in: Add coroutines.o.
	* 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.
	* 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.
---
 gcc/cp/Make-lang.in      |    2 +-
 gcc/cp/call.c            |   21 +
 gcc/cp/config-lang.in    |    2 +-
 gcc/cp/constexpr.c       |    6 +
 gcc/cp/coroutines.cc     | 3589 ++++++++++++++++++++++++++++++++++++++++++++++
 gcc/cp/cp-objcp-common.c |    4 +
 gcc/cp/cp-tree.def       |   24 +
 gcc/cp/cp-tree.h         |    2 +
 gcc/cp/decl.c            |   50 +
 gcc/cp/parser.c          |   89 +-
 gcc/cp/pt.c              |   21 +
 gcc/cp/tree.c            |   31 +
 12 files changed, 3833 insertions(+), 8 deletions(-)
 create mode 100644 gcc/cp/coroutines.cc

diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in
index d94afa44a6..7896591dd4 100644
--- a/gcc/cp/Make-lang.in
+++ b/gcc/cp/Make-lang.in
@@ -73,7 +73,7 @@ CXX_C_OBJS = attribs.o incpath.o \
 # 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 \
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 9a22398e79..11d1299082 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -3172,6 +3172,7 @@ add_builtin_candidates (struct z_candidate **candidates, enum tree_code code,
     case ADDR_EXPR:
     case COMPOUND_EXPR:
     case COMPONENT_REF:
+    case CO_AWAIT_EXPR:
       return;
 
     case COND_EXPR:
@@ -4583,6 +4584,14 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args,
       result = build_over_call (cand, flags, complain);
     }
 
+  if (flag_coroutines
+      && result
+      && result != error_mark_node
+      && 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);
 
@@ -4941,6 +4950,16 @@ op_error (const op_location_t &loc,
 		  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)
@@ -6193,6 +6212,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
 	case ADDR_EXPR:
 	case COMPOUND_EXPR:
 	case COMPONENT_REF:
+	case CO_AWAIT_EXPR:
 	  result = NULL_TREE;
 	  result_valid_p = true;
 	  break;
@@ -6485,6 +6505,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags,
     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:
diff --git a/gcc/cp/config-lang.in b/gcc/cp/config-lang.in
index 47b11df0b2..da70b35841 100644
--- a/gcc/cp/config-lang.in
+++ b/gcc/cp/config-lang.in
@@ -39,7 +39,7 @@ gtfiles="\
 \$(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 \
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index c8852bd930..e7b080cbad 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -7752,6 +7752,12 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
     case ANNOTATE_EXPR:
       return RECUR (TREE_OPERAND (t, 0), rval);
 
+    /* coroutine await 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;
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
new file mode 100644
index 0000000000..2d5b99c1fd
--- /dev/null
+++ b/gcc/cp/coroutines.cc
@@ -0,0 +1,3589 @@
+/* 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_coro_traits_template_class (tree, location_t);
+static tree find_coro_handle_type (location_t, tree);
+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_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 tree coro_traits_identifier;
+static tree coro_handle_identifier;
+static tree coro_promise_type_identifier;
+
+/* Required promise method name identifiers.  */
+
+static tree coro_await_transform_identifier;
+static tree coro_initial_suspend_identifier;
+static tree coro_final_suspend_identifier;
+static tree coro_return_void_identifier;
+static tree coro_return_value_identifier;
+static tree coro_yield_value_identifier;
+static tree coro_resume_identifier;
+static tree coro_from_address_identifier;
+static tree coro_get_return_object_identifier;
+static tree coro_gro_on_allocation_fail_identifier;
+static tree coro_unhandled_exception_identifier;
+
+/* Awaitable methods.  */
+
+static tree coro_await_ready_identifier;
+static tree coro_await_suspend_identifier;
+static 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 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 class instance.
+   Instantiate that for the function signature.  */
+
+static tree
+find_coro_traits_template_class (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_identifier, targ,
+			     /* in_decl */ NULL_TREE,
+			     /* context */ 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_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.  */
+
+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_handle<>  */
+      void_coro_handle_type = find_coro_handle_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 = find_coro_traits_template_class (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 = find_coro_handle_type (loc, coro_info->promise_type);
+      if (handle_type == NULL_TREE)
+	return false;
+
+      /* Instantiate 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 %qs in %qT",
+		IDENTIFIER_POINTER (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_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 or union",
+		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)
+    /* ???: perhaps we should have some way to check that this is actually
+	    a coroutine handle type.  */
+    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,
+				       true /*musthave*/);
+  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.  */
+  if (warn_return_type)
+    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)
+{
+  struct __proxy_replace *data = (struct __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.  */
+  struct __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)
+{
+  struct __coro_ret_data *data = (struct __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)
+{
+  struct __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;
+
+  struct __coro_aw_data *data = (struct __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)
+{
+  struct __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, struct 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, struct __await_xform_data *xform)
+{
+  struct 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.  */
+  struct __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;
+  struct __await_xform_data *xform = (struct __await_xform_data *) d;
+
+  *stmt = transform_await_expr (await_expr, xform);
+  if (*stmt == error_mark_node)
+    *do_subtree = 0;
+  return NULL_TREE;
+}
+
+typedef struct __param_info
+{
+  tree field_id;
+  vec<tree *> *body_uses;
+  tree frame_type;
+} __param_info_t;
+
+typedef struct __local_var_info
+{
+  tree field_id;
+  tree field_idx;
+  tree frame_type;
+  tree captured;
+  location_t def_loc;
+} __local_var_info_t;
+
+/* 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_t> *local_var_uses;
+};
+
+static tree
+transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
+{
+  struct __local_vars_transform *lvd = (struct __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_t &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_t &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_t *local_var_info = lvd->local_var_uses->get (var_decl);
+  if (local_var_info == NULL)
+    return NULL_TREE;
+
+  /* This is our revised 'local' i.e. a frame slot.  */
+  tree revised = local_var_info->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_t> *param_uses,
+		hash_map<tree, __local_var_info_t> *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_t &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.  */
+  struct __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.  */
+
+  struct __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, true /*musthave*/);
+  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));
+}
+
+static bool
+register_await_info (tree await_expr, tree aw_type, tree aw_nam, tree susp_type,
+		     tree susp_handle_nam)
+{
+  bool seen;
+  struct 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);
+  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 (!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));
+
+	  struct __susp_frame_data *data = (struct __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));
+
+	      struct __susp_frame_data *data = (struct __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)
+{
+  struct __susp_frame_data *data = (struct __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)
+{
+  struct __susp_frame_data *awpts = (struct __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);
+	  struct __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;
+  struct __susp_frame_data *awpts = (struct __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_t> *param_uses;
+  location_t loc;
+  bool param_seen;
+};
+
+static tree
+register_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
+{
+  struct __param_frame_data *data = (struct __param_frame_data *) d;
+
+  if (TREE_CODE (*stmt) != PARM_DECL)
+    return NULL_TREE;
+
+  bool existed;
+  __param_info_t &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_t> *local_var_uses;
+  vec<__local_var_info_t> *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)
+{
+  struct __local_vars_frame_data *lvd = (struct __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_t &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)(struct _R_frame *);
+  void (*__destroy)(struct _R_frame *);
+  struct 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.  */
+
+  suspend_points = hash_map<tree, struct suspend_point_info>::create_ggc (11);
+
+  /* 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.  */
+  struct __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_t> *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_t>;
+
+      for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
+	   arg = DECL_CHAIN (arg))
+	{
+	  bool existed;
+	  __param_info_t &parm = param_uses->get_or_insert (arg, &existed);
+	  gcc_checking_assert (!existed);
+	  parm.field_id = NULL_TREE;
+	  parm.body_uses = NULL;
+	}
+
+      struct __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, &param_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_t> local_var_uses;
+  auto_vec<__local_var_info_t> captures;
+
+  struct __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_t *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, false /*musthave*/);
+
+  /* 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, true /*complain*/, 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_t &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_t 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 %qs in %qT",
+		    IDENTIFIER_POINTER (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;
+
+  return true;
+}
+
+#include "gt-cp-coroutines.h"
+
diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index 42eb5ac033..dfd8be978b 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -551,6 +551,10 @@ cp_common_init_ts (void)
   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 ();
 }
 
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 3f96da70e8..1454802bf6 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -574,6 +574,30 @@ DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2)
    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
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 553e447434..5c3caf3c8e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7944,6 +7944,8 @@ extern void cp_ubsan_maybe_initialize_vtbl_ptrs (tree);
 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.  */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 094e32edf5..41c14001fd 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -16783,6 +16783,29 @@ add_return_star_this_fixit (gcc_rich_location *richloc, tree fndecl)
 				       indent);
 }
 
+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);
+  /* Things get weird if we don't start fresh.  */
+  clear_fold_cache ();
+  cp_fold_function (helper);
+  DECL_CONTEXT (DECL_RESULT (helper)) = helper;
+  BLOCK_SUPERCONTEXT (DECL_INITIAL (helper)) = helper;
+  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
@@ -16795,6 +16818,10 @@ finish_function (bool inline_p)
 {
   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.  */
@@ -16821,6 +16848,22 @@ finish_function (bool inline_p)
      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;
+	}
+
+      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))
@@ -17064,6 +17107,13 @@ finish_function (bool inline_p)
       && !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.  */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 7cd8e150dd..d4fb56efc7 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -177,7 +177,9 @@ enum required_token {
   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
@@ -2471,6 +2473,12 @@ static void cp_parser_function_transaction
 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,
@@ -8112,6 +8120,7 @@ cp_parser_pseudo_destructor_name (cp_parser* parser,
      postfix-expression
      ++ cast-expression
      -- cast-expression
+     await-expression
      unary-operator cast-expression
      sizeof unary-expression
      sizeof ( type-id )
@@ -8326,6 +8335,22 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 			    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;
 	}
@@ -9759,6 +9784,7 @@ cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr)
      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.
@@ -9775,6 +9801,10 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
      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
@@ -11273,6 +11303,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
 	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);
@@ -12914,6 +12945,7 @@ cp_parser_init_statement (cp_parser *parser, tree *decl)
      continue ;
      return expression [opt] ;
      return braced-init-list ;
+     coroutine-return-statement;
      goto identifier ;
 
    GNU extension:
@@ -12984,6 +13016,7 @@ cp_parser_jump_statement (cp_parser* parser)
       cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
       break;
 
+    case RID_CO_RETURN:
     case RID_RETURN:
       {
 	tree expr;
@@ -13001,8 +13034,11 @@ cp_parser_jump_statement (cp_parser* parser)
 	  /* 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);
@@ -15383,22 +15419,25 @@ cp_parser_operator (cp_parser* parser, location_t start_loc)
     {
     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);
@@ -26071,6 +26110,41 @@ cp_parser_throw_expression (cp_parser* parser)
   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.
@@ -30315,6 +30389,9 @@ cp_parser_required_error (cp_parser *parser,
       case RT_TRANSACTION_RELAXED:
 	gmsgid = G_("expected %<__transaction_relaxed%>");
 	break;
+      case RT_CO_YIELD:
+	gmsgid = G_("expected %<co_yield%>");
+	break;
       default:
 	break;
     }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index bc23e9e035..9ce66ceab3 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -16728,6 +16728,11 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	 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)
@@ -17594,6 +17599,22 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
       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))
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index d59962c589..21f733af48 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -5068,6 +5068,37 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
 	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;
     }
-- 
2.14.3

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 4/7, v2] Middle end expanders and transforms.
  2019-11-19 10:00         ` [C++ coroutines 4/6] Middle end expanders and transforms Richard Biener
@ 2020-01-09 12:38           ` Iain Sandoe
  2020-01-09 14:38             ` Richard Biener
  0 siblings, 1 reply; 30+ messages in thread
From: Iain Sandoe @ 2020-01-09 12:38 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, libstdc++

Hi Richard,

The SVN commit IDs that address changes to this part of the patchset are noted
in the revised patch header below, for reference.

Richard Biener <richard.guenther@gmail.com> wrote:

> On Sun, Nov 17, 2019 at 11:27 AM Iain Sandoe <iain@sandoe.co.uk> wrote:

>>   Once we have remade the connections to their correct postions, we elide
>>   the labels that the front end inserted.
> 
> Comments inline.

> Do  you actually verify and error for non-INTEGER_CSTs in the
> frontend?  Users can
> inject malicous calls to builtins at their discretion.

Done (it seems that there’s no generic support for such a constraint so
I added a specific validation for this).

>> +           bool dir = wi::to_wide (from) != 0;
>> +           tree vptr = build_pointer_type (void_type_node);
> 
> this is ptr_type_node (also elsewhere)
done throughout.

>> +                               build_int_cst (sizetype, offs));
> 
> size_int  (offs)
done 

>> +           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.  */
>> +           tree vptr = build_pointer_type (void_type_node);
>> +           HOST_WIDE_INT psize = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (vptr));
>> +           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,
>> +                                        wide_int_to_tree (fntype_ppp, offset));
> 
> You are reading a pointer to the coro_resume builtin here?  You are careful
> with using pointer-to-function types but the offset calculation uses the size
> of void * here. 
> Note that the type of the offset operand in a MEM_REF denotes
> TBAA info, so stores to whatever is at *ptr should better have matching types
> (in this light re-using the function type of the builtin might be iffy).

In fact, in general, the object passed to this builtin will be type-erased.  We will
not know the true type of the “promise object” or the handle.  The only facts that
we can rely on here are that the layout contains:

….. something…
void (*)(void*) [resume]
void (*)(void*) [destroy]
promise: <= we only know that this is its start address, not the type.
…. something….

So we could only say at this stage for sure that the extent would be at least one
byte.

** (note) However, we are never trying to manipulate the promise object at this
stage, when ever we _are_ trying to manipulate it, we will have a complete frame
type as part of the call.

Having said that, perhaps I’m still missing a change that could improve this.

> While wide_int_to_tree seems to match HOST_WIDE_INT the canonical
> way across the code-base is to use build_int_cst (type, hwint).
done.

>> +           tree d_ptr_tmp = make_ssa_name (TYPE_MAIN_VARIANT (vptr));
> 
> No need for TYPE_MAIN_VARIANT here if you use ptr_type_node.
fixed.
> 
>> +           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,
>> +                                    wide_int_to_tree (vptr, 0));
> 
> null_pointer_node
fixed

>> +  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);
> 
> it would be nice to elide the function walk for functions not
> containing any CO* stuff (you noted that below for other parts).
> We can spend a bit in struct function noting functions with
> coroutine code inside and set the bit from frontends or from
> the gimplifier for example.  Since it's behind the flag_coroutines
> paywall this can be addressed as followup.

I did this anyway.

>> +  basic_block bb;
> 
> A comment on why we need two IL walks would be nice.  It looks like you
> could have remembered CO_ACTOR calls in the first walk in a vec<gcall *>
> for example and then just process that?

I fixed this - with a worklist.

>> +  if (changed)
>> +    {
>> +      /* 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)
> 
> An extra walk over all BBs looks a bit excessive?  The label_to_block
> map should be set up so you could walk the set and from there
> affected blocks.

I am concerned that we might need to remove labels for coroutine yield
IFNs that have been removed already - and thus are not “seen” above.
For now, I left this as is with the early exit you noted below.
(I guess it would not be hard to make it a worklist for this too)

>> +       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.  */
>> +                 }
>> +             }
>> +           gsi_next (&gsi);
> 
> At least you can stop walking a BB if you hit something else
> than a label.  Thus, else break;

done.

>> +/* Optimize (not yet) and lower frame allocation.
>> +
>> +   This is a place-holder for an optimisation to remove unused frame
>> +   entries and re-size the frame to minimum.  */
> 
> I wonder if this will ever be reasonably possible at this point of the
> compilation.  So instead of having this placeholder please do
> the copy through during one of the lowering phases.

I don’t want to side-track the patch discussion at this juncture:

 a) I removed the pass.
 b) and will just note that the coroutine frame is the direct analogue of the
 activation record; it is entirely under the control of the compiler and each
 entry could be considered as independent as any stack frame entry.  Thus
 there cannot be any hidden aliassing. I’d like to discuss in some separate
 thread the opportunities here (it seems to me to be an entirely different
 problem from the general “reorganise aggregate layout” being discussed
 elsewhere).

>>   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);
> 
> So the two passes above are really close - what's the reason to not
> do coroutine_lower_builtins when the CFG is already built?

The first pass has to run for any function (since the library builtins could and
will be used by non-coroutine functions - e.g. in libstdc++ headers).

The first pass also lowers the IFN_CO_SUSP - which modifies the CFG.

The second pass needs the CFG, actually it would be nice to shift that pass
later in the pipeline to improve the chances of elision of CO_YIELD points via
DCE.

Revised patch below
OK for thrunk now?
thanks
Iain

=======

As described in the covering note, the main part of this is the
expansion of the library support builtins, these are simple boolean
or numerical substitutions.  This pass has to run for non-coroutine
functions, since any 'regular' function may make use of them.

The functionality of implementing an exit from scope without cleanup
is performed here by lowering an IFN to a gimple goto.

The final part is the expansion of the coroutine IFNs that describe the
state machine connections to the dispatchers.  This only has to be run
for functions that are coroutine components.

   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.

Squashed commits.

r278861 - Address review comments, use ptr_type_node
r278864 - Address review comments, more standard nodes and APIs
r278884 - Address review comments, remove co_frame lowering pass.
r278936 - Address review comments, make a more specific pass gate.
r279098 - Address review comments, exit early where possible.
r279405 - Address review comments, more whitespace and comment changes.
r279726 - Address review comments, remove one iteration through BBs.
r279740 - Re-indent lower_coro_builtin.
r279741 - Remove unnecessary control flow, re-indent.
r279817 - Update copyright year.
r280035 - Fix trailing whitespace.

gcc/ChangeLog:

2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>

	* Makefile.in: Add coroutine-passes.o.
	* coroutine-passes.cc: New file.
	* function.h (struct GTY function): Add a bit to indicate that the
	function is a coroutine component.
	* 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.

gcc/cp/ChangeLog:

2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>

	* decl.c (emit_coro_helper): Set a bit to signal that this is a
	coroutine component.
	(finish_function): Likewise.
---
 gcc/Makefile.in         |   1 +
 gcc/coroutine-passes.cc | 532 ++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/cp/decl.c           |   6 +
 gcc/function.h          |   3 +
 gcc/passes.def          |   2 +
 gcc/tree-pass.h         |   2 +
 6 files changed, 546 insertions(+)
 create mode 100644 gcc/coroutine-passes.cc

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 61b512c9b7..0849ce4826 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1265,6 +1265,7 @@ OBJS = \
 	compare-elim.o \
 	context.o \
 	convert.o \
+	coroutine-passes.o \
 	coverage.o \
 	cppbuiltin.o \
 	cppdefault.o \
diff --git a/gcc/coroutine-passes.cc b/gcc/coroutine-passes.cc
new file mode 100644
index 0000000000..d032a392ce
--- /dev/null
+++ b/gcc/coroutine-passes.cc
@@ -0,0 +1,532 @@
+/* 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);
+}
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 41c14001fd..6093613bae 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -16802,6 +16802,9 @@ emit_coro_helper (tree helper)
   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);
 }
@@ -16858,6 +16861,9 @@ finish_function (bool inline_p)
 	  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)),
diff --git a/gcc/function.h b/gcc/function.h
index 496d3f728c..1ee8ed3de5 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -418,6 +418,9 @@ struct GTY(()) function {
   /* 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.  */
diff --git a/gcc/passes.def b/gcc/passes.def
index b6bd4f3d31..2db4ef4acc 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -39,8 +39,10 @@ along with GCC; see the file COPYING3.  If not see
   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);
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 5ff43572b8..f10f467462 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -478,6 +478,8 @@ extern gimple_opt_pass *make_pass_gen_hsail (gcc::context *ctxt);
 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);
-- 
2.14.3

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 5/7, v2] Standard library header.
  2019-11-17 16:19           ` [C++ coroutines 5/6] Standard library header Jonathan Wakely
@ 2020-01-09 12:39             ` Iain Sandoe
  2020-01-09 13:50               ` Jonathan Wakely
  0 siblings, 1 reply; 30+ messages in thread
From: Iain Sandoe @ 2020-01-09 12:39 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: GCC Patches, libstdc++

Hi Jonathan

The SVN commit IDs that relate to the amendments are noted in the
revised patch header below.

Jonathan Wakely <jwakely@redhat.com> wrote:

> On 17/11/19 10:27 +0000, Iain Sandoe wrote:

>> 	* include/Makefile.in: Regnerated.
> 
> "Regnerated" typo.
Fixed,

>> 	${experimental_srcdir}/chrono \
>> +	${experimental_srcdir}/coroutine \
> 
> The experimental dir is (currently) only used for TS headers. All
> C++20 support is currently experimental, so adding <coroutines> where
> <concepts> and <ranges> have been added would be OK.
> 
> But I'm not really clear if this is an implementation of the TS or the
> C++20 feature.  If it's a hybrid, putting it in
> <experimental/coroutines> is fine.

Mea culpa;
this began life as a TS and when it was included in the WD (and then CD)
I failed to move the header and update.

My intention is that the facility will work with at least C++14 as a GNU
extension (as do the impls. in clang and EDG).  In fact, I can’t see any
real reason that it wouldn’t work with C++11 - but time is too short for
that experiment.

> When the final <coroutines> header is added it will need to be in
> libsupc++ so that it's included for freestanding builds (and at that
> point it won't be able to use <bits/stl_function.h>, but that will be
> OK as the final header will be C++20-only and can rely on <compare>
> unconditionally, which is also freestanding).

Well, for the reason above, I have made this not depend on the availability
of <compare> or spaceship.

>> +/** @file experimental/coroutine
>> + *  This is an experimental C++ Library header against the C++20 CD n4835.
>> + *  @ingroup coroutine-ts
> 
> The coroutine-ts doc group should be defined somewhere.

I’ve reworked the doxygen stuff somewhat, although freely admit that it’s currently
rather weak as documentation.  I do expect some more editorial changes to the std
and documentation could be improved before 10 releases.

>> +  namespace experimental {
>> +  inline namespace coroutines_n4835 {
> 
> This should be a reserved name too, e.g. __coroutines_n4835.
done
>> 
>> +    constexpr coroutine_handle () noexcept : __fr_ptr (0) {}
> 
> The libstdc++ naming convention is _M_xxx for non-static members (both
> data members and member functions) and _S_xxx for static members
> (again, both data member and functions).
> 
> This helps to distinguish members from other uglified names like
> function parameters and local variables.
done.

Hopefully, this amended version is closer and the intent is clearer.
OK for trunk now?
thanks
Iain

———

This provides the interfaces mandated by the standard and implements
the interaction with the coroutine frame by means of inline use of
builtins expanded at compile-time.  There should be a 1:1 correspondence
with the standard sections which are cross-referenced.

There is no runtime content.

At this stage we have the content in an inline namespace "__n4835" for
the current CD.

Squashed commits:

r278724 - Address review comments, GNU naming conventions
r278778 - Address review comments, update doxygen group info.
r279817 - Update copyright year.
r279845 - Address review comments, move coroutine header to std.
r280038 - Correct pathname in the edit header.

libstdc++-v3/ChangeLog:

2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>

	* include/Makefile.am: Add coroutine to the std set.
	* include/Makefile.in: Regenerated.
	* include/std/coroutine: New file.
---
 libstdc++-v3/include/Makefile.am   |   1 +
 libstdc++-v3/include/Makefile.in   |   1 +
 libstdc++-v3/include/std/coroutine | 269 +++++++++++++++++++++++++++++++++++++
 3 files changed, 271 insertions(+)
 create mode 100644 libstdc++-v3/include/std/coroutine

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index b38defcafb..ad4404793b 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -38,6 +38,7 @@ std_headers = \
 	${std_srcdir}/complex \
 	${std_srcdir}/concepts \
 	${std_srcdir}/condition_variable \
+	${std_srcdir}/coroutine \
 	${std_srcdir}/deque \
 	${std_srcdir}/execution \
 	${std_srcdir}/filesystem \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index ae4a493ea6..f8b5645224 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -382,6 +382,7 @@ std_headers = \
 	${std_srcdir}/complex \
 	${std_srcdir}/concepts \
 	${std_srcdir}/condition_variable \
+	${std_srcdir}/coroutine \
 	${std_srcdir}/deque \
 	${std_srcdir}/execution \
 	${std_srcdir}/filesystem \
diff --git a/libstdc++-v3/include/std/coroutine b/libstdc++-v3/include/std/coroutine
new file mode 100644
index 0000000000..9eaffaba12
--- /dev/null
+++ b/libstdc++-v3/include/std/coroutine
@@ -0,0 +1,269 @@
+// <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_EXPERIMENTAL_COROUTINE
+#define _GLIBCXX_EXPERIMENTAL_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>
+#  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 _R, typename...> struct coroutine_traits
+  {
+    using promise_type = typename _R::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 (decltype (nullptr) __h) noexcept
+      : _M_fr_ptr (__h)
+    {}
+    coroutine_handle &operator= (decltype (nullptr)) 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;
+#else
+  // These are from the TS 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 <class _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= (decltype (nullptr)) 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 <>
+  class 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
+    // constexpr void* address() const noexcept;
+  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_GLIBCXX_VISIBILITY(default)
+
+#endif // C++14 (we are allowing use from at least this)
+
+#endif // _GLIBCXX_EXPERIMENTAL_COROUTINE
-- 
2.14.3


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 6/7, v2] Testsuite.
  2019-11-19 10:01             ` Richard Biener
@ 2020-01-09 12:40               ` Iain Sandoe
  2020-01-09 13:00                 ` Iain Sandoe
  2020-01-09 13:17                 ` Richard Biener
  0 siblings, 2 replies; 30+ messages in thread
From: Iain Sandoe @ 2020-01-09 12:40 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, libstdc++

Richard Biener <richard.guenther@gmail.com> wrote:

> On Sun, Nov 17, 2019 at 11:29 AM Iain Sandoe <iain@sandoe.co.uk> wrote:
>> There are two categories of test:
>> 
>> 1. Checks for correctly formed source code and the error reporting.
>> 2. Checks for transformation and code-gen.
>> 
>> The second set are run as 'torture' tests for the standard options
>> set, including LTO.  These are also intentionally run with no options
>> provided (from the coroutines.exp script).
> 
> OK once the rest is approved.

Amended version attached, for the record, with cleanups and more tests as
needed to cover the other changes in the series.

SVN commit IDs noted in the revised header.
Still OK?
Iain

======

There are two categories of test:

1. Checks for correctly formed source code and the error reporting.
2. Checks for transformation and code-gen.

The second set are run as 'torture' tests for the standard options
set, including LTO.  These are also intentionally run with no options
provided (from the coroutines.exp script).

Squashed commits:

r278441 - Remove reference to an unused builtin from coro.h
r278442 - Fix a code-gen error in coro_maybe_expand_co_return.
r278453 - Address review comments, updated error message,
r278480 - Address review comments, new tests
r278776 - JAddress review comments, update to make move of coro header easier
r279020 - Fix bug in handling co_return co_await.
r279046 - more tidyup, also factor code.
r279049 - add more co-await tests and rename to consistent.
r279050 - Add more co-yield syntax tests.
r279078 - More testsuite refactoring.
r279080 - Fix a bug in type dependency checks.
r279095 - More testsuite refactoring.
r279099 - Update operator overload tests.
r279190 - Update co-return tests.
r279191 - Just fix a format warning.
r279198 - Improve test coverage, initial class tests.
r279245 - Test syntax error from lambda with auto return.
r279318 - Test function-like lambdas.
r279383 - Use correct dg options for new compile tests.
r279385 - Fix PR 92933 unnamed locals.
r279396 - Fix lambda capture of references.
r279403 - Add testcase to cover non-constant param refs.
r279462 - Fix a bug where await_resume methods return references.
r279693 - Rename co-await testcases to be consistent [NFC].
r279694 - Rename co-return testcases to be consistent [NFC].
r279711 - Rename co-yield testcases to be consistent [NFC].
r279712 - Rename func params testcases to be consistent [NFC].
r279719 - Fix a typo in a testcase name.
r279805 - Address review comments, prepare to move coroutine header.
r279817 - Update copyright year.
r280004 - Add promise overloads of new and delete.

gcc/testsuite/ChangeLog:

2020-01-09  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.
---
 .../coroutines/co-await-syntax-00-needs-expr.C     |   7 +
 .../coroutines/co-await-syntax-01-outside-fn.C     |   5 +
 .../coroutines/co-await-syntax-02-outside-fn.C     |   5 +
 .../g++.dg/coroutines/co-await-syntax-03-auto.C    |  16 ++
 .../coroutines/co-await-syntax-04-ctor-dtor.C      |   8 +
 .../coroutines/co-await-syntax-05-constexpr.C      |  12 ++
 .../g++.dg/coroutines/co-await-syntax-06-main.C    |   7 +
 .../g++.dg/coroutines/co-await-syntax-07-varargs.C |  14 ++
 .../coroutines/co-await-syntax-08-lambda-auto.C    |  19 +++
 .../coroutines/co-return-syntax-01-outside-fn.C    |   6 +
 .../coroutines/co-return-syntax-02-outside-fn.C    |   5 +
 .../g++.dg/coroutines/co-return-syntax-03-auto.C   |  12 ++
 .../coroutines/co-return-syntax-04-ctor-dtor.C     |   8 +
 .../coroutines/co-return-syntax-05-constexpr-fn.C  |  12 ++
 .../g++.dg/coroutines/co-return-syntax-06-main.C   |   7 +
 .../g++.dg/coroutines/co-return-syntax-07-vararg.C |  14 ++
 .../coroutines/co-return-syntax-08-bad-return.C    |  43 ++++++
 .../coroutines/co-return-syntax-09-lambda-auto.C   |  19 +++
 .../coroutines/co-yield-syntax-00-needs-expr.C     |   7 +
 .../coroutines/co-yield-syntax-01-outside-fn.C     |   6 +
 .../coroutines/co-yield-syntax-02-outside-fn.C     |   6 +
 .../g++.dg/coroutines/co-yield-syntax-03-auto.C    |  12 ++
 .../coroutines/co-yield-syntax-04-ctor-dtor.C      |   8 +
 .../coroutines/co-yield-syntax-05-constexpr.C      |  12 ++
 .../g++.dg/coroutines/co-yield-syntax-06-main.C    |   7 +
 .../g++.dg/coroutines/co-yield-syntax-07-varargs.C |  14 ++
 .../coroutines/co-yield-syntax-08-needs-expr.C     |  37 +++++
 .../coroutines/co-yield-syntax-09-lambda-auto.C    |  19 +++
 gcc/testsuite/g++.dg/coroutines/coro-builtins.C    |  17 +++
 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C |  32 ++++
 .../g++.dg/coroutines/coro-missing-promise-yield.C |  33 ++++
 .../g++.dg/coroutines/coro-missing-ret-value.C     |  34 +++++
 .../g++.dg/coroutines/coro-missing-ret-void.C      |  34 +++++
 .../g++.dg/coroutines/coro-missing-ueh-1.C         |  17 +++
 .../g++.dg/coroutines/coro-missing-ueh-2.C         |  18 +++
 .../g++.dg/coroutines/coro-missing-ueh-3.C         |  18 +++
 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h |  23 +++
 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C    |   9 ++
 gcc/testsuite/g++.dg/coroutines/coro.h             | 151 +++++++++++++++++++
 .../g++.dg/coroutines/coro1-ret-int-yield-int.h    | 133 ++++++++++++++++
 gcc/testsuite/g++.dg/coroutines/coroutines.exp     |  50 ++++++
 .../torture/alloc-00-gro-on-alloc-fail.C           | 118 +++++++++++++++
 .../coroutines/torture/alloc-01-overload-newdel.C  | 120 +++++++++++++++
 .../g++.dg/coroutines/torture/call-00-co-aw-arg.C  |  73 +++++++++
 .../coroutines/torture/call-01-multiple-co-aw.C    |  73 +++++++++
 .../g++.dg/coroutines/torture/call-02-temp-co-aw.C |  72 +++++++++
 .../coroutines/torture/call-03-temp-ref-co-aw.C    |  72 +++++++++
 .../g++.dg/coroutines/torture/class-00-co-ret.C    |  41 +++++
 .../coroutines/torture/class-01-co-ret-parm.C      |  57 +++++++
 .../coroutines/torture/class-02-templ-parm.C       |  52 +++++++
 .../torture/class-03-operator-templ-parm.C         |  52 +++++++
 .../g++.dg/coroutines/torture/class-04-lambda-1.C  |  58 +++++++
 .../torture/class-05-lambda-capture-copy-local.C   |  59 ++++++++
 .../torture/class-06-lambda-capture-ref.C          |  59 ++++++++
 .../coroutines/torture/co-await-00-trivial.C       |  52 +++++++
 .../coroutines/torture/co-await-01-with-value.C    |  57 +++++++
 .../g++.dg/coroutines/torture/co-await-02-xform.C  |  58 +++++++
 .../g++.dg/coroutines/torture/co-await-03-rhs-op.C |  58 +++++++
 .../coroutines/torture/co-await-04-control-flow.C  |  50 ++++++
 .../g++.dg/coroutines/torture/co-await-05-loop.C   |  51 +++++++
 .../g++.dg/coroutines/torture/co-await-06-ovl.C    |  65 ++++++++
 .../g++.dg/coroutines/torture/co-await-07-tmpl.C   | 132 ++++++++++++++++
 .../coroutines/torture/co-await-08-cascade.C       |  63 ++++++++
 .../g++.dg/coroutines/torture/co-await-09-pair.C   |  57 +++++++
 .../torture/co-await-10-template-fn-arg.C          |  60 ++++++++
 .../coroutines/torture/co-await-11-forwarding.C    |  43 ++++++
 .../coroutines/torture/co-await-12-operator-2.C    |  66 ++++++++
 .../coroutines/torture/co-await-13-return-ref.C    |  58 +++++++
 .../torture/co-ret-00-void-return-is-ready.C       |  90 +++++++++++
 .../torture/co-ret-01-void-return-is-suspend.C     |  94 ++++++++++++
 .../torture/co-ret-03-different-GRO-type.C         |  92 ++++++++++++
 .../coroutines/torture/co-ret-04-GRO-nontriv.C     | 109 ++++++++++++++
 .../coroutines/torture/co-ret-05-return-value.C    |  38 +++++
 .../torture/co-ret-06-template-promise-val-1.C     | 105 +++++++++++++
 .../coroutines/torture/co-ret-07-void-cast-expr.C  |  44 ++++++
 .../torture/co-ret-08-template-cast-ret.C          | 104 +++++++++++++
 .../coroutines/torture/co-ret-09-bool-await-susp.C |  97 ++++++++++++
 .../torture/co-ret-10-expression-evaluates-once.C  |  49 ++++++
 .../coroutines/torture/co-ret-11-co-ret-co-await.C |  40 +++++
 .../torture/co-ret-12-co-ret-fun-co-await.C        |  48 ++++++
 .../coroutines/torture/co-ret-13-template-2.C      |  56 +++++++
 .../coroutines/torture/co-ret-14-template-3.C      |  58 +++++++
 .../g++.dg/coroutines/torture/co-yield-00-triv.C   | 129 ++++++++++++++++
 .../g++.dg/coroutines/torture/co-yield-01-multi.C  |  64 ++++++++
 .../g++.dg/coroutines/torture/co-yield-02-loop.C   |  68 +++++++++
 .../g++.dg/coroutines/torture/co-yield-03-tmpl.C   | 140 +++++++++++++++++
 .../torture/co-yield-04-complex-local-state.C      | 162 ++++++++++++++++++++
 .../g++.dg/coroutines/torture/co-yield-05-co-aw.C  |  55 +++++++
 .../coroutines/torture/co-yield-06-fun-parm.C      |  64 ++++++++
 .../torture/co-yield-07-template-fn-param.C        |  71 +++++++++
 .../coroutines/torture/co-yield-08-more-refs.C     |  68 +++++++++
 .../torture/co-yield-09-more-templ-refs.C          |  68 +++++++++
 .../g++.dg/coroutines/torture/coro-torture.exp     |  19 +++
 .../g++.dg/coroutines/torture/exceptions-test-0.C  | 167 +++++++++++++++++++++
 .../g++.dg/coroutines/torture/func-params-00.C     |  42 ++++++
 .../g++.dg/coroutines/torture/func-params-01.C     |  45 ++++++
 .../g++.dg/coroutines/torture/func-params-02.C     |  50 ++++++
 .../g++.dg/coroutines/torture/func-params-03.C     |  49 ++++++
 .../g++.dg/coroutines/torture/func-params-04.C     |  57 +++++++
 .../g++.dg/coroutines/torture/func-params-05.C     |  57 +++++++
 .../g++.dg/coroutines/torture/func-params-06.C     |  47 ++++++
 .../g++.dg/coroutines/torture/lambda-00-co-ret.C   |  35 +++++
 .../coroutines/torture/lambda-01-co-ret-parm.C     |  48 ++++++
 .../coroutines/torture/lambda-02-co-yield-values.C |  64 ++++++++
 .../coroutines/torture/lambda-03-auto-parm-1.C     |  46 ++++++
 .../coroutines/torture/lambda-04-templ-parm.C      |  47 ++++++
 .../torture/lambda-05-capture-copy-local.C         |  66 ++++++++
 .../coroutines/torture/lambda-06-multi-capture.C   |  48 ++++++
 .../coroutines/torture/lambda-07-multi-yield.C     |  46 ++++++
 .../coroutines/torture/lambda-08-co-ret-parm-ref.C |  59 ++++++++
 .../g++.dg/coroutines/torture/local-var-0.C        |  37 +++++
 .../g++.dg/coroutines/torture/local-var-1.C        |  37 +++++
 .../g++.dg/coroutines/torture/local-var-2.C        |  50 ++++++
 .../g++.dg/coroutines/torture/local-var-3.C        |  65 ++++++++
 .../g++.dg/coroutines/torture/local-var-4.C        |  75 +++++++++
 .../coroutines/torture/mid-suspend-destruction-0.C | 107 +++++++++++++
 gcc/testsuite/g++.dg/coroutines/torture/pr92933.C  |  18 +++
 117 files changed, 5986 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-builtins.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro.h
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coroutines.exp
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/pr92933.C

diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C
new file mode 100644
index 0000000000..d068c3d19a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C
@@ -0,0 +1,7 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+void bar () {
+  co_await;  // { dg-error "expected primary-expression before" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C
new file mode 100644
index 0000000000..484859c706
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C
@@ -0,0 +1,5 @@
+//  { 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} }
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C
new file mode 100644
index 0000000000..4ce5c2e04a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C
@@ -0,0 +1,5 @@
+//  { 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} }
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
new file mode 100644
index 0000000000..7f4ed9afef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
@@ -0,0 +1,16 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C
new file mode 100644
index 0000000000..ac0ba2e54f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C
@@ -0,0 +1,8 @@
+//  { 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" }
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C
new file mode 100644
index 0000000000..73a0b1499d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C
@@ -0,0 +1,12 @@
+//  { 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 ();
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C
new file mode 100644
index 0000000000..ab520baaff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C
@@ -0,0 +1,7 @@
+//  { 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" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
new file mode 100644
index 0000000000..4e41dd3be4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
@@ -0,0 +1,14 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C
new file mode 100644
index 0000000000..61db5feed3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C
@@ -0,0 +1,19 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C
new file mode 100644
index 0000000000..3fcd8dd104
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C
@@ -0,0 +1,6 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+co_return; // { dg-error {expected unqualified-id before 'co_return'} }
+
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C
new file mode 100644
index 0000000000..cda36eb2a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C
@@ -0,0 +1,5 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+auto f (co_return); // { dg-error {expected primary-expression before 'co_return'} }
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
new file mode 100644
index 0000000000..93a04dc459
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
@@ -0,0 +1,12 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C
new file mode 100644
index 0000000000..9396432e8b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C
@@ -0,0 +1,8 @@
+//  { 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" }
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C
new file mode 100644
index 0000000000..69b109fb60
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C
@@ -0,0 +1,12 @@
+//  { 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 ();
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C
new file mode 100644
index 0000000000..40d7e4e362
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C
@@ -0,0 +1,7 @@
+//  { 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" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
new file mode 100644
index 0000000000..0aea17a1db
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
@@ -0,0 +1,14 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
new file mode 100644
index 0000000000..4bfa41cd4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
@@ -0,0 +1,43 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C
new file mode 100644
index 0000000000..8fe52361ba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C
@@ -0,0 +1,19 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C
new file mode 100644
index 0000000000..547f1a31c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C
@@ -0,0 +1,7 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+void foo () {
+  co_yield;  // { dg-error "expected primary-expression before" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C
new file mode 100644
index 0000000000..30db0e963b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C
@@ -0,0 +1,6 @@
+//  { 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} }
+
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C
new file mode 100644
index 0000000000..71e119fbef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C
@@ -0,0 +1,6 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int a[] = { co_yield 21 }; // { dg-error {'co_yield' cannot be used outside a function} }
+
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
new file mode 100644
index 0000000000..808a07f5e1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
@@ -0,0 +1,12 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C
new file mode 100644
index 0000000000..cc46e01d77
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C
@@ -0,0 +1,8 @@
+//  { 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" }
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C
new file mode 100644
index 0000000000..39ef19c63b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C
@@ -0,0 +1,12 @@
+//  { 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 ();
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C
new file mode 100644
index 0000000000..dcc3dbce75
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C
@@ -0,0 +1,7 @@
+//  { 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" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
new file mode 100644
index 0000000000..f0b568335e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
@@ -0,0 +1,14 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C
new file mode 100644
index 0000000000..86969f781e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C
@@ -0,0 +1,37 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C
new file mode 100644
index 0000000000..5190face00
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C
@@ -0,0 +1,19 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-builtins.C b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
new file mode 100644
index 0000000000..d7c4883384
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
@@ -0,0 +1,17 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
new file mode 100644
index 0000000000..fb02e9d580
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
@@ -0,0 +1,32 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
new file mode 100644
index 0000000000..d489c3953a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
@@ -0,0 +1,33 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
new file mode 100644
index 0000000000..f238c4b9a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
@@ -0,0 +1,34 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
new file mode 100644
index 0000000000..c9f84e5902
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
@@ -0,0 +1,34 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
new file mode 100644
index 0000000000..3943e78d9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
@@ -0,0 +1,17 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
new file mode 100644
index 0000000000..0f105c4c2d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
@@ -0,0 +1,18 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
new file mode 100644
index 0000000000..d775d8a630
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
@@ -0,0 +1,18 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
new file mode 100644
index 0000000000..51e6135b8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
@@ -0,0 +1,23 @@
+#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
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
new file mode 100644
index 0000000000..f22a5e0833
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
@@ -0,0 +1,9 @@
+// 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
diff --git a/gcc/testsuite/g++.dg/coroutines/coro.h b/gcc/testsuite/g++.dg/coroutines/coro.h
new file mode 100644
index 0000000000..ca12d2689e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro.h
@@ -0,0 +1,151 @@
+#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 *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
diff --git a/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h b/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
new file mode 100644
index 0000000000..b961755e47
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
@@ -0,0 +1,133 @@
+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;
+  };
+
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/coroutines.exp b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
new file mode 100644
index 0000000000..e7fd4dac46
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
@@ -0,0 +1,50 @@
+#   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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C b/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C
new file mode 100644
index 0000000000..8430d053c6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C
@@ -0,0 +1,118 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C b/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C
new file mode 100644
index 0000000000..f779f6e486
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C
@@ -0,0 +1,120 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C b/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
new file mode 100644
index 0000000000..ee108072f6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
@@ -0,0 +1,73 @@
+// { 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C
new file mode 100644
index 0000000000..0f5785163f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C
@@ -0,0 +1,73 @@
+// { 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
new file mode 100644
index 0000000000..4982c49d79
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
@@ -0,0 +1,72 @@
+// { 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C
new file mode 100644
index 0000000000..d0bb4667ac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C
@@ -0,0 +1,72 @@
+// { 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
new file mode 100644
index 0000000000..932fe4b283
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
@@ -0,0 +1,41 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C
new file mode 100644
index 0000000000..0bd477044b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C
@@ -0,0 +1,57 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C
new file mode 100644
index 0000000000..0cc6069c32
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C
@@ -0,0 +1,52 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C
new file mode 100644
index 0000000000..2d888a7455
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C
@@ -0,0 +1,52 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C b/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
new file mode 100644
index 0000000000..e191c20ac0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
@@ -0,0 +1,58 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C b/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C
new file mode 100644
index 0000000000..968940f505
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C
@@ -0,0 +1,59 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C
new file mode 100644
index 0000000000..db60132b0e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C
@@ -0,0 +1,59 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C
new file mode 100644
index 0000000000..a24c261599
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C
@@ -0,0 +1,52 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C
new file mode 100644
index 0000000000..db5c90224d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C
@@ -0,0 +1,57 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
new file mode 100644
index 0000000000..79ee6e1714
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
@@ -0,0 +1,58 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
new file mode 100644
index 0000000000..6408432573
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
@@ -0,0 +1,58 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C
new file mode 100644
index 0000000000..9bc99e875d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C
@@ -0,0 +1,50 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
new file mode 100644
index 0000000000..34af740c99
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
@@ -0,0 +1,51 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
new file mode 100644
index 0000000000..14945faffd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
@@ -0,0 +1,65 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
new file mode 100644
index 0000000000..33f8e99d8c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
@@ -0,0 +1,132 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C
new file mode 100644
index 0000000000..d34619d6b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C
@@ -0,0 +1,63 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
new file mode 100644
index 0000000000..525c6fc467
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
@@ -0,0 +1,57 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C
new file mode 100644
index 0000000000..71a5b18c3c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C
@@ -0,0 +1,60 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C
new file mode 100644
index 0000000000..78c88ed14e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C
@@ -0,0 +1,43 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C
new file mode 100644
index 0000000000..189332b78e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C
@@ -0,0 +1,66 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C
new file mode 100644
index 0000000000..339ebe4ff2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C
@@ -0,0 +1,58 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C
new file mode 100644
index 0000000000..f551c6e760
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C
@@ -0,0 +1,90 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C
new file mode 100644
index 0000000000..03fc6eeb84
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C
@@ -0,0 +1,94 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C
new file mode 100644
index 0000000000..36da680f7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C
@@ -0,0 +1,92 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C
new file mode 100644
index 0000000000..29fb9424f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C
@@ -0,0 +1,109 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C
new file mode 100644
index 0000000000..42b80ff6bb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C
@@ -0,0 +1,38 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C
new file mode 100644
index 0000000000..5b1acb8145
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C
@@ -0,0 +1,105 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C
new file mode 100644
index 0000000000..b1a06f2849
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C
@@ -0,0 +1,44 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C
new file mode 100644
index 0000000000..266bc7b3b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C
@@ -0,0 +1,104 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C
new file mode 100644
index 0000000000..91f3f14cc0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C
@@ -0,0 +1,97 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C
new file mode 100644
index 0000000000..7b07be5f44
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C
@@ -0,0 +1,49 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C
new file mode 100644
index 0000000000..06939107d8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C
@@ -0,0 +1,40 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C
new file mode 100644
index 0000000000..50124c080b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C
@@ -0,0 +1,48 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C
new file mode 100644
index 0000000000..9d4a4de8eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C
@@ -0,0 +1,56 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C
new file mode 100644
index 0000000000..ebc1adba82
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C
@@ -0,0 +1,58 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C
new file mode 100644
index 0000000000..586b6b2571
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C
@@ -0,0 +1,129 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C
new file mode 100644
index 0000000000..5df69c7f15
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C
@@ -0,0 +1,64 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C
new file mode 100644
index 0000000000..8d4f1d5d82
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C
@@ -0,0 +1,68 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C
new file mode 100644
index 0000000000..cceee1f19e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C
@@ -0,0 +1,140 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C
new file mode 100644
index 0000000000..d9330b33b7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C
@@ -0,0 +1,162 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C
new file mode 100644
index 0000000000..043f97b6e1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C
@@ -0,0 +1,55 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C
new file mode 100644
index 0000000000..c74e44d15d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C
@@ -0,0 +1,64 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C
new file mode 100644
index 0000000000..74dae63395
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C
@@ -0,0 +1,71 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C
new file mode 100644
index 0000000000..8e39127a1a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C
@@ -0,0 +1,68 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C
new file mode 100644
index 0000000000..3abbe1c43a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C
@@ -0,0 +1,68 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
new file mode 100644
index 0000000000..d2463b2798
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
@@ -0,0 +1,19 @@
+# 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
new file mode 100644
index 0000000000..164c804797
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
@@ -0,0 +1,167 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C
new file mode 100644
index 0000000000..b5716972d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C
@@ -0,0 +1,42 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C
new file mode 100644
index 0000000000..f530431a6b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C
@@ -0,0 +1,45 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C
new file mode 100644
index 0000000000..396b438cb2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C
@@ -0,0 +1,50 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C
new file mode 100644
index 0000000000..bf699722a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C
@@ -0,0 +1,49 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C
new file mode 100644
index 0000000000..789e2c05b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C
@@ -0,0 +1,57 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C
new file mode 100644
index 0000000000..8bdb2b5d0f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C
@@ -0,0 +1,57 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C
new file mode 100644
index 0000000000..cbcfe67ff1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C
@@ -0,0 +1,47 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C
new file mode 100644
index 0000000000..61e284d5c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C
@@ -0,0 +1,35 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C
new file mode 100644
index 0000000000..378eedc6d8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C
@@ -0,0 +1,48 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C
new file mode 100644
index 0000000000..a6f592cd77
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C
@@ -0,0 +1,64 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C
new file mode 100644
index 0000000000..bfa5400225
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C
@@ -0,0 +1,46 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C
new file mode 100644
index 0000000000..adf31e22db
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C
@@ -0,0 +1,47 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C
new file mode 100644
index 0000000000..7cd6648cca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C
@@ -0,0 +1,66 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C
new file mode 100644
index 0000000000..7b445d3d9c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C
@@ -0,0 +1,48 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C
new file mode 100644
index 0000000000..2bd58cbf2e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C
@@ -0,0 +1,46 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C
new file mode 100644
index 0000000000..4d5a44fe29
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C
@@ -0,0 +1,59 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
new file mode 100644
index 0000000000..a8956457dc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
@@ -0,0 +1,37 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
new file mode 100644
index 0000000000..69a5b70756
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
@@ -0,0 +1,37 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
new file mode 100644
index 0000000000..f232edabda
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
@@ -0,0 +1,50 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
new file mode 100644
index 0000000000..bd06db53d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
@@ -0,0 +1,65 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
new file mode 100644
index 0000000000..419eb6b646
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
@@ -0,0 +1,75 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
new file mode 100644
index 0000000000..934fb19de7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
@@ -0,0 +1,107 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C b/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C
new file mode 100644
index 0000000000..b2f1be78b3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C
@@ -0,0 +1,18 @@
+//  { 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) {
+    }
+}
-- 
2.14.3

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [C++ coroutines 6/7, v2] Testsuite.
  2020-01-09 12:40               ` [C++ coroutines 6/7, v2] Testsuite Iain Sandoe
@ 2020-01-09 13:00                 ` Iain Sandoe
  2020-01-09 13:17                 ` Richard Biener
  1 sibling, 0 replies; 30+ messages in thread
From: Iain Sandoe @ 2020-01-09 13:00 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches, libstdc++

Richard Biener <richard.guenther@gmail.com> wrote:

> On Sun, Nov 17, 2019 at 11:29 AM Iain Sandoe <iain@sandoe.co.uk> wrote:
>> There are two categories of test:
>> 
>> 1. Checks for correctly formed source code and the error reporting.
>> 2. Checks for transformation and code-gen.
>> 
>> The second set are run as 'torture' tests for the standard options
>> set, including LTO.  These are also intentionally run with no options
>> provided (from the coroutines.exp script).
> 
> OK once the rest is approved.

Amended version attached, for the record, with cleanups and more tests as
needed to cover the other changes in the series.

SVN commit IDs noted in the revised header.
Still OK?
Iain

======

There are two categories of test:

1. Checks for correctly formed source code and the error reporting.
2. Checks for transformation and code-gen.

The second set are run as 'torture' tests for the standard options
set, including LTO.  These are also intentionally run with no options
provided (from the coroutines.exp script).

Squashed commits:

r278441 - Remove reference to an unused builtin from coro.h
r278442 - Fix a code-gen error in coro_maybe_expand_co_return.
r278453 - Address review comments, updated error message,
r278480 - Address review comments, new tests
r278776 - JAddress review comments, update to make move of coro header easier
r279020 - Fix bug in handling co_return co_await.
r279046 - more tidyup, also factor code.
r279049 - add more co-await tests and rename to consistent.
r279050 - Add more co-yield syntax tests.
r279078 - More testsuite refactoring.
r279080 - Fix a bug in type dependency checks.
r279095 - More testsuite refactoring.
r279099 - Update operator overload tests.
r279190 - Update co-return tests.
r279191 - Just fix a format warning.
r279198 - Improve test coverage, initial class tests.
r279245 - Test syntax error from lambda with auto return.
r279318 - Test function-like lambdas.
r279383 - Use correct dg options for new compile tests.
r279385 - Fix PR 92933 unnamed locals.
r279396 - Fix lambda capture of references.
r279403 - Add testcase to cover non-constant param refs.
r279462 - Fix a bug where await_resume methods return references.
r279693 - Rename co-await testcases to be consistent [NFC].
r279694 - Rename co-return testcases to be consistent [NFC].
r279711 - Rename co-yield testcases to be consistent [NFC].
r279712 - Rename func params testcases to be consistent [NFC].
r279719 - Fix a typo in a testcase name.
r279805 - Address review comments, prepare to move coroutine header.
r279817 - Update copyright year.
r280004 - Add promise overloads of new and delete.

gcc/testsuite/ChangeLog:

2020-01-09  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.
---
 .../coroutines/co-await-syntax-00-needs-expr.C     |   7 +
 .../coroutines/co-await-syntax-01-outside-fn.C     |   5 +
 .../coroutines/co-await-syntax-02-outside-fn.C     |   5 +
 .../g++.dg/coroutines/co-await-syntax-03-auto.C    |  16 ++
 .../coroutines/co-await-syntax-04-ctor-dtor.C      |   8 +
 .../coroutines/co-await-syntax-05-constexpr.C      |  12 ++
 .../g++.dg/coroutines/co-await-syntax-06-main.C    |   7 +
 .../g++.dg/coroutines/co-await-syntax-07-varargs.C |  14 ++
 .../coroutines/co-await-syntax-08-lambda-auto.C    |  19 +++
 .../coroutines/co-return-syntax-01-outside-fn.C    |   6 +
 .../coroutines/co-return-syntax-02-outside-fn.C    |   5 +
 .../g++.dg/coroutines/co-return-syntax-03-auto.C   |  12 ++
 .../coroutines/co-return-syntax-04-ctor-dtor.C     |   8 +
 .../coroutines/co-return-syntax-05-constexpr-fn.C  |  12 ++
 .../g++.dg/coroutines/co-return-syntax-06-main.C   |   7 +
 .../g++.dg/coroutines/co-return-syntax-07-vararg.C |  14 ++
 .../coroutines/co-return-syntax-08-bad-return.C    |  43 ++++++
 .../coroutines/co-return-syntax-09-lambda-auto.C   |  19 +++
 .../coroutines/co-yield-syntax-00-needs-expr.C     |   7 +
 .../coroutines/co-yield-syntax-01-outside-fn.C     |   6 +
 .../coroutines/co-yield-syntax-02-outside-fn.C     |   6 +
 .../g++.dg/coroutines/co-yield-syntax-03-auto.C    |  12 ++
 .../coroutines/co-yield-syntax-04-ctor-dtor.C      |   8 +
 .../coroutines/co-yield-syntax-05-constexpr.C      |  12 ++
 .../g++.dg/coroutines/co-yield-syntax-06-main.C    |   7 +
 .../g++.dg/coroutines/co-yield-syntax-07-varargs.C |  14 ++
 .../coroutines/co-yield-syntax-08-needs-expr.C     |  37 +++++
 .../coroutines/co-yield-syntax-09-lambda-auto.C    |  19 +++
 gcc/testsuite/g++.dg/coroutines/coro-builtins.C    |  17 +++
 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C |  32 ++++
 .../g++.dg/coroutines/coro-missing-promise-yield.C |  33 ++++
 .../g++.dg/coroutines/coro-missing-ret-value.C     |  34 +++++
 .../g++.dg/coroutines/coro-missing-ret-void.C      |  34 +++++
 .../g++.dg/coroutines/coro-missing-ueh-1.C         |  17 +++
 .../g++.dg/coroutines/coro-missing-ueh-2.C         |  18 +++
 .../g++.dg/coroutines/coro-missing-ueh-3.C         |  18 +++
 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h |  23 +++
 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C    |   9 ++
 gcc/testsuite/g++.dg/coroutines/coro.h             | 151 +++++++++++++++++++
 .../g++.dg/coroutines/coro1-ret-int-yield-int.h    | 133 ++++++++++++++++
 gcc/testsuite/g++.dg/coroutines/coroutines.exp     |  50 ++++++
 .../torture/alloc-00-gro-on-alloc-fail.C           | 118 +++++++++++++++
 .../coroutines/torture/alloc-01-overload-newdel.C  | 120 +++++++++++++++
 .../g++.dg/coroutines/torture/call-00-co-aw-arg.C  |  73 +++++++++
 .../coroutines/torture/call-01-multiple-co-aw.C    |  73 +++++++++
 .../g++.dg/coroutines/torture/call-02-temp-co-aw.C |  72 +++++++++
 .../coroutines/torture/call-03-temp-ref-co-aw.C    |  72 +++++++++
 .../g++.dg/coroutines/torture/class-00-co-ret.C    |  41 +++++
 .../coroutines/torture/class-01-co-ret-parm.C      |  57 +++++++
 .../coroutines/torture/class-02-templ-parm.C       |  52 +++++++
 .../torture/class-03-operator-templ-parm.C         |  52 +++++++
 .../g++.dg/coroutines/torture/class-04-lambda-1.C  |  58 +++++++
 .../torture/class-05-lambda-capture-copy-local.C   |  59 ++++++++
 .../torture/class-06-lambda-capture-ref.C          |  59 ++++++++
 .../coroutines/torture/co-await-00-trivial.C       |  52 +++++++
 .../coroutines/torture/co-await-01-with-value.C    |  57 +++++++
 .../g++.dg/coroutines/torture/co-await-02-xform.C  |  58 +++++++
 .../g++.dg/coroutines/torture/co-await-03-rhs-op.C |  58 +++++++
 .../coroutines/torture/co-await-04-control-flow.C  |  50 ++++++
 .../g++.dg/coroutines/torture/co-await-05-loop.C   |  51 +++++++
 .../g++.dg/coroutines/torture/co-await-06-ovl.C    |  65 ++++++++
 .../g++.dg/coroutines/torture/co-await-07-tmpl.C   | 132 ++++++++++++++++
 .../coroutines/torture/co-await-08-cascade.C       |  63 ++++++++
 .../g++.dg/coroutines/torture/co-await-09-pair.C   |  57 +++++++
 .../torture/co-await-10-template-fn-arg.C          |  60 ++++++++
 .../coroutines/torture/co-await-11-forwarding.C    |  43 ++++++
 .../coroutines/torture/co-await-12-operator-2.C    |  66 ++++++++
 .../coroutines/torture/co-await-13-return-ref.C    |  58 +++++++
 .../torture/co-ret-00-void-return-is-ready.C       |  90 +++++++++++
 .../torture/co-ret-01-void-return-is-suspend.C     |  94 ++++++++++++
 .../torture/co-ret-03-different-GRO-type.C         |  92 ++++++++++++
 .../coroutines/torture/co-ret-04-GRO-nontriv.C     | 109 ++++++++++++++
 .../coroutines/torture/co-ret-05-return-value.C    |  38 +++++
 .../torture/co-ret-06-template-promise-val-1.C     | 105 +++++++++++++
 .../coroutines/torture/co-ret-07-void-cast-expr.C  |  44 ++++++
 .../torture/co-ret-08-template-cast-ret.C          | 104 +++++++++++++
 .../coroutines/torture/co-ret-09-bool-await-susp.C |  97 ++++++++++++
 .../torture/co-ret-10-expression-evaluates-once.C  |  49 ++++++
 .../coroutines/torture/co-ret-11-co-ret-co-await.C |  40 +++++
 .../torture/co-ret-12-co-ret-fun-co-await.C        |  48 ++++++
 .../coroutines/torture/co-ret-13-template-2.C      |  56 +++++++
 .../coroutines/torture/co-ret-14-template-3.C      |  58 +++++++
 .../g++.dg/coroutines/torture/co-yield-00-triv.C   | 129 ++++++++++++++++
 .../g++.dg/coroutines/torture/co-yield-01-multi.C  |  64 ++++++++
 .../g++.dg/coroutines/torture/co-yield-02-loop.C   |  68 +++++++++
 .../g++.dg/coroutines/torture/co-yield-03-tmpl.C   | 140 +++++++++++++++++
 .../torture/co-yield-04-complex-local-state.C      | 162 ++++++++++++++++++++
 .../g++.dg/coroutines/torture/co-yield-05-co-aw.C  |  55 +++++++
 .../coroutines/torture/co-yield-06-fun-parm.C      |  64 ++++++++
 .../torture/co-yield-07-template-fn-param.C        |  71 +++++++++
 .../coroutines/torture/co-yield-08-more-refs.C     |  68 +++++++++
 .../torture/co-yield-09-more-templ-refs.C          |  68 +++++++++
 .../g++.dg/coroutines/torture/coro-torture.exp     |  19 +++
 .../g++.dg/coroutines/torture/exceptions-test-0.C  | 167 +++++++++++++++++++++
 .../g++.dg/coroutines/torture/func-params-00.C     |  42 ++++++
 .../g++.dg/coroutines/torture/func-params-01.C     |  45 ++++++
 .../g++.dg/coroutines/torture/func-params-02.C     |  50 ++++++
 .../g++.dg/coroutines/torture/func-params-03.C     |  49 ++++++
 .../g++.dg/coroutines/torture/func-params-04.C     |  57 +++++++
 .../g++.dg/coroutines/torture/func-params-05.C     |  57 +++++++
 .../g++.dg/coroutines/torture/func-params-06.C     |  47 ++++++
 .../g++.dg/coroutines/torture/lambda-00-co-ret.C   |  35 +++++
 .../coroutines/torture/lambda-01-co-ret-parm.C     |  48 ++++++
 .../coroutines/torture/lambda-02-co-yield-values.C |  64 ++++++++
 .../coroutines/torture/lambda-03-auto-parm-1.C     |  46 ++++++
 .../coroutines/torture/lambda-04-templ-parm.C      |  47 ++++++
 .../torture/lambda-05-capture-copy-local.C         |  66 ++++++++
 .../coroutines/torture/lambda-06-multi-capture.C   |  48 ++++++
 .../coroutines/torture/lambda-07-multi-yield.C     |  46 ++++++
 .../coroutines/torture/lambda-08-co-ret-parm-ref.C |  59 ++++++++
 .../g++.dg/coroutines/torture/local-var-0.C        |  37 +++++
 .../g++.dg/coroutines/torture/local-var-1.C        |  37 +++++
 .../g++.dg/coroutines/torture/local-var-2.C        |  50 ++++++
 .../g++.dg/coroutines/torture/local-var-3.C        |  65 ++++++++
 .../g++.dg/coroutines/torture/local-var-4.C        |  75 +++++++++
 .../coroutines/torture/mid-suspend-destruction-0.C | 107 +++++++++++++
 gcc/testsuite/g++.dg/coroutines/torture/pr92933.C  |  18 +++
 117 files changed, 5986 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-builtins.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro.h
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
 create mode 100644 gcc/testsuite/g++.dg/coroutines/coroutines.exp
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/pr92933.C

diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C
new file mode 100644
index 0000000000..d068c3d19a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C
@@ -0,0 +1,7 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+void bar () {
+  co_await;  // { dg-error "expected primary-expression before" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C
new file mode 100644
index 0000000000..484859c706
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C
@@ -0,0 +1,5 @@
+//  { 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} }
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C
new file mode 100644
index 0000000000..4ce5c2e04a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C
@@ -0,0 +1,5 @@
+//  { 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} }
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
new file mode 100644
index 0000000000..7f4ed9afef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
@@ -0,0 +1,16 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C
new file mode 100644
index 0000000000..ac0ba2e54f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C
@@ -0,0 +1,8 @@
+//  { 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" }
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C
new file mode 100644
index 0000000000..73a0b1499d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C
@@ -0,0 +1,12 @@
+//  { 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 ();
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C
new file mode 100644
index 0000000000..ab520baaff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C
@@ -0,0 +1,7 @@
+//  { 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" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
new file mode 100644
index 0000000000..4e41dd3be4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
@@ -0,0 +1,14 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C
new file mode 100644
index 0000000000..61db5feed3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C
@@ -0,0 +1,19 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C
new file mode 100644
index 0000000000..3fcd8dd104
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C
@@ -0,0 +1,6 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+co_return; // { dg-error {expected unqualified-id before 'co_return'} }
+
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C
new file mode 100644
index 0000000000..cda36eb2a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C
@@ -0,0 +1,5 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+auto f (co_return); // { dg-error {expected primary-expression before 'co_return'} }
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
new file mode 100644
index 0000000000..93a04dc459
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
@@ -0,0 +1,12 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C
new file mode 100644
index 0000000000..9396432e8b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C
@@ -0,0 +1,8 @@
+//  { 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" }
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C
new file mode 100644
index 0000000000..69b109fb60
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C
@@ -0,0 +1,12 @@
+//  { 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 ();
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C
new file mode 100644
index 0000000000..40d7e4e362
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C
@@ -0,0 +1,7 @@
+//  { 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" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
new file mode 100644
index 0000000000..0aea17a1db
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
@@ -0,0 +1,14 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
new file mode 100644
index 0000000000..4bfa41cd4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
@@ -0,0 +1,43 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C
new file mode 100644
index 0000000000..8fe52361ba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C
@@ -0,0 +1,19 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C
new file mode 100644
index 0000000000..547f1a31c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C
@@ -0,0 +1,7 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+void foo () {
+  co_yield;  // { dg-error "expected primary-expression before" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C
new file mode 100644
index 0000000000..30db0e963b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C
@@ -0,0 +1,6 @@
+//  { 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} }
+
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C
new file mode 100644
index 0000000000..71e119fbef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C
@@ -0,0 +1,6 @@
+//  { dg-additional-options "-fsyntax-only -w" }
+
+#include "coro.h"
+
+int a[] = { co_yield 21 }; // { dg-error {'co_yield' cannot be used outside a function} }
+
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
new file mode 100644
index 0000000000..808a07f5e1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
@@ -0,0 +1,12 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C
new file mode 100644
index 0000000000..cc46e01d77
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C
@@ -0,0 +1,8 @@
+//  { 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" }
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C
new file mode 100644
index 0000000000..39ef19c63b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C
@@ -0,0 +1,12 @@
+//  { 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 ();
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C
new file mode 100644
index 0000000000..dcc3dbce75
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C
@@ -0,0 +1,7 @@
+//  { 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" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
new file mode 100644
index 0000000000..f0b568335e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
@@ -0,0 +1,14 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C
new file mode 100644
index 0000000000..86969f781e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C
@@ -0,0 +1,37 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C
new file mode 100644
index 0000000000..5190face00
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C
@@ -0,0 +1,19 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-builtins.C b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
new file mode 100644
index 0000000000..d7c4883384
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
@@ -0,0 +1,17 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
new file mode 100644
index 0000000000..fb02e9d580
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
@@ -0,0 +1,32 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
new file mode 100644
index 0000000000..d489c3953a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
@@ -0,0 +1,33 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
new file mode 100644
index 0000000000..f238c4b9a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
@@ -0,0 +1,34 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
new file mode 100644
index 0000000000..c9f84e5902
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
@@ -0,0 +1,34 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
new file mode 100644
index 0000000000..3943e78d9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
@@ -0,0 +1,17 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
new file mode 100644
index 0000000000..0f105c4c2d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
@@ -0,0 +1,18 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
new file mode 100644
index 0000000000..d775d8a630
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
@@ -0,0 +1,18 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
new file mode 100644
index 0000000000..51e6135b8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
@@ -0,0 +1,23 @@
+#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
diff --git a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
new file mode 100644
index 0000000000..f22a5e0833
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
@@ -0,0 +1,9 @@
+// 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
diff --git a/gcc/testsuite/g++.dg/coroutines/coro.h b/gcc/testsuite/g++.dg/coroutines/coro.h
new file mode 100644
index 0000000000..ca12d2689e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro.h
@@ -0,0 +1,151 @@
+#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 *, ...);
+extern "C" void abort (void) __attribute__((__noreturn__));
+
+#ifndef OUTPUT
+#  define PRINT(X)
+#  define PRINTF (void)
+#else
+#  define PRINT(X) puts(X)
+#  define PRINTF printf
+#endif
diff --git a/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h b/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
new file mode 100644
index 0000000000..b961755e47
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
@@ -0,0 +1,133 @@
+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;
+  };
+
+};
diff --git a/gcc/testsuite/g++.dg/coroutines/coroutines.exp b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
new file mode 100644
index 0000000000..e7fd4dac46
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
@@ -0,0 +1,50 @@
+#   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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C b/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C
new file mode 100644
index 0000000000..8430d053c6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C
@@ -0,0 +1,118 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C b/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C
new file mode 100644
index 0000000000..f779f6e486
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C
@@ -0,0 +1,120 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C b/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
new file mode 100644
index 0000000000..ee108072f6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
@@ -0,0 +1,73 @@
+// { 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C
new file mode 100644
index 0000000000..0f5785163f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C
@@ -0,0 +1,73 @@
+// { 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
new file mode 100644
index 0000000000..4982c49d79
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
@@ -0,0 +1,72 @@
+// { 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C
new file mode 100644
index 0000000000..d0bb4667ac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C
@@ -0,0 +1,72 @@
+// { 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
new file mode 100644
index 0000000000..932fe4b283
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
@@ -0,0 +1,41 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C
new file mode 100644
index 0000000000..0bd477044b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C
@@ -0,0 +1,57 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C
new file mode 100644
index 0000000000..0cc6069c32
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C
@@ -0,0 +1,52 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C
new file mode 100644
index 0000000000..2d888a7455
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C
@@ -0,0 +1,52 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C b/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
new file mode 100644
index 0000000000..e191c20ac0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
@@ -0,0 +1,58 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C b/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C
new file mode 100644
index 0000000000..968940f505
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C
@@ -0,0 +1,59 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C
new file mode 100644
index 0000000000..db60132b0e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C
@@ -0,0 +1,59 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C
new file mode 100644
index 0000000000..a24c261599
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C
@@ -0,0 +1,52 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C
new file mode 100644
index 0000000000..db5c90224d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C
@@ -0,0 +1,57 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
new file mode 100644
index 0000000000..79ee6e1714
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
@@ -0,0 +1,58 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
new file mode 100644
index 0000000000..6408432573
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
@@ -0,0 +1,58 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C
new file mode 100644
index 0000000000..9bc99e875d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C
@@ -0,0 +1,50 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
new file mode 100644
index 0000000000..34af740c99
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
@@ -0,0 +1,51 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
new file mode 100644
index 0000000000..14945faffd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
@@ -0,0 +1,65 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
new file mode 100644
index 0000000000..33f8e99d8c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
@@ -0,0 +1,132 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C
new file mode 100644
index 0000000000..d34619d6b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C
@@ -0,0 +1,63 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
new file mode 100644
index 0000000000..525c6fc467
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
@@ -0,0 +1,57 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C
new file mode 100644
index 0000000000..71a5b18c3c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C
@@ -0,0 +1,60 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C
new file mode 100644
index 0000000000..78c88ed14e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C
@@ -0,0 +1,43 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C
new file mode 100644
index 0000000000..189332b78e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C
@@ -0,0 +1,66 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C
new file mode 100644
index 0000000000..339ebe4ff2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C
@@ -0,0 +1,58 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C
new file mode 100644
index 0000000000..f551c6e760
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C
@@ -0,0 +1,90 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C
new file mode 100644
index 0000000000..03fc6eeb84
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C
@@ -0,0 +1,94 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C
new file mode 100644
index 0000000000..36da680f7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C
@@ -0,0 +1,92 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C
new file mode 100644
index 0000000000..29fb9424f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C
@@ -0,0 +1,109 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C
new file mode 100644
index 0000000000..42b80ff6bb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C
@@ -0,0 +1,38 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C
new file mode 100644
index 0000000000..5b1acb8145
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C
@@ -0,0 +1,105 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C
new file mode 100644
index 0000000000..b1a06f2849
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C
@@ -0,0 +1,44 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C
new file mode 100644
index 0000000000..266bc7b3b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C
@@ -0,0 +1,104 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C
new file mode 100644
index 0000000000..91f3f14cc0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C
@@ -0,0 +1,97 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C
new file mode 100644
index 0000000000..7b07be5f44
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C
@@ -0,0 +1,49 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C
new file mode 100644
index 0000000000..06939107d8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C
@@ -0,0 +1,40 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C
new file mode 100644
index 0000000000..50124c080b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C
@@ -0,0 +1,48 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C
new file mode 100644
index 0000000000..9d4a4de8eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C
@@ -0,0 +1,56 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C
new file mode 100644
index 0000000000..ebc1adba82
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C
@@ -0,0 +1,58 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C
new file mode 100644
index 0000000000..586b6b2571
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C
@@ -0,0 +1,129 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C
new file mode 100644
index 0000000000..5df69c7f15
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C
@@ -0,0 +1,64 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C
new file mode 100644
index 0000000000..8d4f1d5d82
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C
@@ -0,0 +1,68 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C
new file mode 100644
index 0000000000..cceee1f19e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C
@@ -0,0 +1,140 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C
new file mode 100644
index 0000000000..d9330b33b7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C
@@ -0,0 +1,162 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C
new file mode 100644
index 0000000000..043f97b6e1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C
@@ -0,0 +1,55 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C
new file mode 100644
index 0000000000..c74e44d15d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C
@@ -0,0 +1,64 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C
new file mode 100644
index 0000000000..74dae63395
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C
@@ -0,0 +1,71 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C
new file mode 100644
index 0000000000..8e39127a1a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C
@@ -0,0 +1,68 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C
new file mode 100644
index 0000000000..3abbe1c43a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C
@@ -0,0 +1,68 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
new file mode 100644
index 0000000000..d2463b2798
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
@@ -0,0 +1,19 @@
+# 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
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
new file mode 100644
index 0000000000..164c804797
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
@@ -0,0 +1,167 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C
new file mode 100644
index 0000000000..b5716972d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C
@@ -0,0 +1,42 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C
new file mode 100644
index 0000000000..f530431a6b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C
@@ -0,0 +1,45 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C
new file mode 100644
index 0000000000..396b438cb2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C
@@ -0,0 +1,50 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C
new file mode 100644
index 0000000000..bf699722a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C
@@ -0,0 +1,49 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C
new file mode 100644
index 0000000000..789e2c05b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C
@@ -0,0 +1,57 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C
new file mode 100644
index 0000000000..8bdb2b5d0f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C
@@ -0,0 +1,57 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C
new file mode 100644
index 0000000000..cbcfe67ff1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C
@@ -0,0 +1,47 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C
new file mode 100644
index 0000000000..61e284d5c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C
@@ -0,0 +1,35 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C
new file mode 100644
index 0000000000..378eedc6d8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C
@@ -0,0 +1,48 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C
new file mode 100644
index 0000000000..a6f592cd77
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C
@@ -0,0 +1,64 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C
new file mode 100644
index 0000000000..bfa5400225
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C
@@ -0,0 +1,46 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C
new file mode 100644
index 0000000000..adf31e22db
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C
@@ -0,0 +1,47 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C
new file mode 100644
index 0000000000..7cd6648cca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C
@@ -0,0 +1,66 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C
new file mode 100644
index 0000000000..7b445d3d9c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C
@@ -0,0 +1,48 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C
new file mode 100644
index 0000000000..2bd58cbf2e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C
@@ -0,0 +1,46 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C
new file mode 100644
index 0000000000..4d5a44fe29
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C
@@ -0,0 +1,59 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
new file mode 100644
index 0000000000..a8956457dc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
@@ -0,0 +1,37 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
new file mode 100644
index 0000000000..69a5b70756
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
@@ -0,0 +1,37 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
new file mode 100644
index 0000000000..f232edabda
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
@@ -0,0 +1,50 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
new file mode 100644
index 0000000000..bd06db53d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
@@ -0,0 +1,65 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
new file mode 100644
index 0000000000..419eb6b646
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
@@ -0,0 +1,75 @@
+//  { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
new file mode 100644
index 0000000000..934fb19de7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
@@ -0,0 +1,107 @@
+// { 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;
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C b/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C
new file mode 100644
index 0000000000..b2f1be78b3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C
@@ -0,0 +1,18 @@
+//  { 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) {
+    }
+}
-- 
2.14.3

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 6/7, v2] Testsuite.
  2020-01-09 12:40               ` [C++ coroutines 6/7, v2] Testsuite Iain Sandoe
  2020-01-09 13:00                 ` Iain Sandoe
@ 2020-01-09 13:17                 ` Richard Biener
  1 sibling, 0 replies; 30+ messages in thread
From: Richard Biener @ 2020-01-09 13:17 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches, libstdc++

On Thu, Jan 9, 2020 at 1:39 PM Iain Sandoe <iain@sandoe.co.uk> wrote:
>
> Richard Biener <richard.guenther@gmail.com> wrote:
>
> > On Sun, Nov 17, 2019 at 11:29 AM Iain Sandoe <iain@sandoe.co.uk> wrote:
> >> There are two categories of test:
> >>
> >> 1. Checks for correctly formed source code and the error reporting.
> >> 2. Checks for transformation and code-gen.
> >>
> >> The second set are run as 'torture' tests for the standard options
> >> set, including LTO.  These are also intentionally run with no options
> >> provided (from the coroutines.exp script).
> >
> > OK once the rest is approved.
>
> Amended version attached, for the record, with cleanups and more tests as
> needed to cover the other changes in the series.
>
> SVN commit IDs noted in the revised header.
> Still OK?

Sure.

> Iain
>
> ======
>
> There are two categories of test:
>
> 1. Checks for correctly formed source code and the error reporting.
> 2. Checks for transformation and code-gen.
>
> The second set are run as 'torture' tests for the standard options
> set, including LTO.  These are also intentionally run with no options
> provided (from the coroutines.exp script).
>
> Squashed commits:
>
> r278441 - Remove reference to an unused builtin from coro.h
> r278442 - Fix a code-gen error in coro_maybe_expand_co_return.
> r278453 - Address review comments, updated error message,
> r278480 - Address review comments, new tests
> r278776 - JAddress review comments, update to make move of coro header easier
> r279020 - Fix bug in handling co_return co_await.
> r279046 - more tidyup, also factor code.
> r279049 - add more co-await tests and rename to consistent.
> r279050 - Add more co-yield syntax tests.
> r279078 - More testsuite refactoring.
> r279080 - Fix a bug in type dependency checks.
> r279095 - More testsuite refactoring.
> r279099 - Update operator overload tests.
> r279190 - Update co-return tests.
> r279191 - Just fix a format warning.
> r279198 - Improve test coverage, initial class tests.
> r279245 - Test syntax error from lambda with auto return.
> r279318 - Test function-like lambdas.
> r279383 - Use correct dg options for new compile tests.
> r279385 - Fix PR 92933 unnamed locals.
> r279396 - Fix lambda capture of references.
> r279403 - Add testcase to cover non-constant param refs.
> r279462 - Fix a bug where await_resume methods return references.
> r279693 - Rename co-await testcases to be consistent [NFC].
> r279694 - Rename co-return testcases to be consistent [NFC].
> r279711 - Rename co-yield testcases to be consistent [NFC].
> r279712 - Rename func params testcases to be consistent [NFC].
> r279719 - Fix a typo in a testcase name.
> r279805 - Address review comments, prepare to move coroutine header.
> r279817 - Update copyright year.
> r280004 - Add promise overloads of new and delete.
>
> gcc/testsuite/ChangeLog:
>
> 2020-01-09  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.
> ---
>  .../coroutines/co-await-syntax-00-needs-expr.C     |   7 +
>  .../coroutines/co-await-syntax-01-outside-fn.C     |   5 +
>  .../coroutines/co-await-syntax-02-outside-fn.C     |   5 +
>  .../g++.dg/coroutines/co-await-syntax-03-auto.C    |  16 ++
>  .../coroutines/co-await-syntax-04-ctor-dtor.C      |   8 +
>  .../coroutines/co-await-syntax-05-constexpr.C      |  12 ++
>  .../g++.dg/coroutines/co-await-syntax-06-main.C    |   7 +
>  .../g++.dg/coroutines/co-await-syntax-07-varargs.C |  14 ++
>  .../coroutines/co-await-syntax-08-lambda-auto.C    |  19 +++
>  .../coroutines/co-return-syntax-01-outside-fn.C    |   6 +
>  .../coroutines/co-return-syntax-02-outside-fn.C    |   5 +
>  .../g++.dg/coroutines/co-return-syntax-03-auto.C   |  12 ++
>  .../coroutines/co-return-syntax-04-ctor-dtor.C     |   8 +
>  .../coroutines/co-return-syntax-05-constexpr-fn.C  |  12 ++
>  .../g++.dg/coroutines/co-return-syntax-06-main.C   |   7 +
>  .../g++.dg/coroutines/co-return-syntax-07-vararg.C |  14 ++
>  .../coroutines/co-return-syntax-08-bad-return.C    |  43 ++++++
>  .../coroutines/co-return-syntax-09-lambda-auto.C   |  19 +++
>  .../coroutines/co-yield-syntax-00-needs-expr.C     |   7 +
>  .../coroutines/co-yield-syntax-01-outside-fn.C     |   6 +
>  .../coroutines/co-yield-syntax-02-outside-fn.C     |   6 +
>  .../g++.dg/coroutines/co-yield-syntax-03-auto.C    |  12 ++
>  .../coroutines/co-yield-syntax-04-ctor-dtor.C      |   8 +
>  .../coroutines/co-yield-syntax-05-constexpr.C      |  12 ++
>  .../g++.dg/coroutines/co-yield-syntax-06-main.C    |   7 +
>  .../g++.dg/coroutines/co-yield-syntax-07-varargs.C |  14 ++
>  .../coroutines/co-yield-syntax-08-needs-expr.C     |  37 +++++
>  .../coroutines/co-yield-syntax-09-lambda-auto.C    |  19 +++
>  gcc/testsuite/g++.dg/coroutines/coro-builtins.C    |  17 +++
>  gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C |  32 ++++
>  .../g++.dg/coroutines/coro-missing-promise-yield.C |  33 ++++
>  .../g++.dg/coroutines/coro-missing-ret-value.C     |  34 +++++
>  .../g++.dg/coroutines/coro-missing-ret-void.C      |  34 +++++
>  .../g++.dg/coroutines/coro-missing-ueh-1.C         |  17 +++
>  .../g++.dg/coroutines/coro-missing-ueh-2.C         |  18 +++
>  .../g++.dg/coroutines/coro-missing-ueh-3.C         |  18 +++
>  gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h |  23 +++
>  gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C    |   9 ++
>  gcc/testsuite/g++.dg/coroutines/coro.h             | 151 +++++++++++++++++++
>  .../g++.dg/coroutines/coro1-ret-int-yield-int.h    | 133 ++++++++++++++++
>  gcc/testsuite/g++.dg/coroutines/coroutines.exp     |  50 ++++++
>  .../torture/alloc-00-gro-on-alloc-fail.C           | 118 +++++++++++++++
>  .../coroutines/torture/alloc-01-overload-newdel.C  | 120 +++++++++++++++
>  .../g++.dg/coroutines/torture/call-00-co-aw-arg.C  |  73 +++++++++
>  .../coroutines/torture/call-01-multiple-co-aw.C    |  73 +++++++++
>  .../g++.dg/coroutines/torture/call-02-temp-co-aw.C |  72 +++++++++
>  .../coroutines/torture/call-03-temp-ref-co-aw.C    |  72 +++++++++
>  .../g++.dg/coroutines/torture/class-00-co-ret.C    |  41 +++++
>  .../coroutines/torture/class-01-co-ret-parm.C      |  57 +++++++
>  .../coroutines/torture/class-02-templ-parm.C       |  52 +++++++
>  .../torture/class-03-operator-templ-parm.C         |  52 +++++++
>  .../g++.dg/coroutines/torture/class-04-lambda-1.C  |  58 +++++++
>  .../torture/class-05-lambda-capture-copy-local.C   |  59 ++++++++
>  .../torture/class-06-lambda-capture-ref.C          |  59 ++++++++
>  .../coroutines/torture/co-await-00-trivial.C       |  52 +++++++
>  .../coroutines/torture/co-await-01-with-value.C    |  57 +++++++
>  .../g++.dg/coroutines/torture/co-await-02-xform.C  |  58 +++++++
>  .../g++.dg/coroutines/torture/co-await-03-rhs-op.C |  58 +++++++
>  .../coroutines/torture/co-await-04-control-flow.C  |  50 ++++++
>  .../g++.dg/coroutines/torture/co-await-05-loop.C   |  51 +++++++
>  .../g++.dg/coroutines/torture/co-await-06-ovl.C    |  65 ++++++++
>  .../g++.dg/coroutines/torture/co-await-07-tmpl.C   | 132 ++++++++++++++++
>  .../coroutines/torture/co-await-08-cascade.C       |  63 ++++++++
>  .../g++.dg/coroutines/torture/co-await-09-pair.C   |  57 +++++++
>  .../torture/co-await-10-template-fn-arg.C          |  60 ++++++++
>  .../coroutines/torture/co-await-11-forwarding.C    |  43 ++++++
>  .../coroutines/torture/co-await-12-operator-2.C    |  66 ++++++++
>  .../coroutines/torture/co-await-13-return-ref.C    |  58 +++++++
>  .../torture/co-ret-00-void-return-is-ready.C       |  90 +++++++++++
>  .../torture/co-ret-01-void-return-is-suspend.C     |  94 ++++++++++++
>  .../torture/co-ret-03-different-GRO-type.C         |  92 ++++++++++++
>  .../coroutines/torture/co-ret-04-GRO-nontriv.C     | 109 ++++++++++++++
>  .../coroutines/torture/co-ret-05-return-value.C    |  38 +++++
>  .../torture/co-ret-06-template-promise-val-1.C     | 105 +++++++++++++
>  .../coroutines/torture/co-ret-07-void-cast-expr.C  |  44 ++++++
>  .../torture/co-ret-08-template-cast-ret.C          | 104 +++++++++++++
>  .../coroutines/torture/co-ret-09-bool-await-susp.C |  97 ++++++++++++
>  .../torture/co-ret-10-expression-evaluates-once.C  |  49 ++++++
>  .../coroutines/torture/co-ret-11-co-ret-co-await.C |  40 +++++
>  .../torture/co-ret-12-co-ret-fun-co-await.C        |  48 ++++++
>  .../coroutines/torture/co-ret-13-template-2.C      |  56 +++++++
>  .../coroutines/torture/co-ret-14-template-3.C      |  58 +++++++
>  .../g++.dg/coroutines/torture/co-yield-00-triv.C   | 129 ++++++++++++++++
>  .../g++.dg/coroutines/torture/co-yield-01-multi.C  |  64 ++++++++
>  .../g++.dg/coroutines/torture/co-yield-02-loop.C   |  68 +++++++++
>  .../g++.dg/coroutines/torture/co-yield-03-tmpl.C   | 140 +++++++++++++++++
>  .../torture/co-yield-04-complex-local-state.C      | 162 ++++++++++++++++++++
>  .../g++.dg/coroutines/torture/co-yield-05-co-aw.C  |  55 +++++++
>  .../coroutines/torture/co-yield-06-fun-parm.C      |  64 ++++++++
>  .../torture/co-yield-07-template-fn-param.C        |  71 +++++++++
>  .../coroutines/torture/co-yield-08-more-refs.C     |  68 +++++++++
>  .../torture/co-yield-09-more-templ-refs.C          |  68 +++++++++
>  .../g++.dg/coroutines/torture/coro-torture.exp     |  19 +++
>  .../g++.dg/coroutines/torture/exceptions-test-0.C  | 167 +++++++++++++++++++++
>  .../g++.dg/coroutines/torture/func-params-00.C     |  42 ++++++
>  .../g++.dg/coroutines/torture/func-params-01.C     |  45 ++++++
>  .../g++.dg/coroutines/torture/func-params-02.C     |  50 ++++++
>  .../g++.dg/coroutines/torture/func-params-03.C     |  49 ++++++
>  .../g++.dg/coroutines/torture/func-params-04.C     |  57 +++++++
>  .../g++.dg/coroutines/torture/func-params-05.C     |  57 +++++++
>  .../g++.dg/coroutines/torture/func-params-06.C     |  47 ++++++
>  .../g++.dg/coroutines/torture/lambda-00-co-ret.C   |  35 +++++
>  .../coroutines/torture/lambda-01-co-ret-parm.C     |  48 ++++++
>  .../coroutines/torture/lambda-02-co-yield-values.C |  64 ++++++++
>  .../coroutines/torture/lambda-03-auto-parm-1.C     |  46 ++++++
>  .../coroutines/torture/lambda-04-templ-parm.C      |  47 ++++++
>  .../torture/lambda-05-capture-copy-local.C         |  66 ++++++++
>  .../coroutines/torture/lambda-06-multi-capture.C   |  48 ++++++
>  .../coroutines/torture/lambda-07-multi-yield.C     |  46 ++++++
>  .../coroutines/torture/lambda-08-co-ret-parm-ref.C |  59 ++++++++
>  .../g++.dg/coroutines/torture/local-var-0.C        |  37 +++++
>  .../g++.dg/coroutines/torture/local-var-1.C        |  37 +++++
>  .../g++.dg/coroutines/torture/local-var-2.C        |  50 ++++++
>  .../g++.dg/coroutines/torture/local-var-3.C        |  65 ++++++++
>  .../g++.dg/coroutines/torture/local-var-4.C        |  75 +++++++++
>  .../coroutines/torture/mid-suspend-destruction-0.C | 107 +++++++++++++
>  gcc/testsuite/g++.dg/coroutines/torture/pr92933.C  |  18 +++
>  117 files changed, 5986 insertions(+)
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-builtins.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro.h
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/coroutines.exp
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
>  create mode 100644 gcc/testsuite/g++.dg/coroutines/torture/pr92933.C
>
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C
> new file mode 100644
> index 0000000000..d068c3d19a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-00-needs-expr.C
> @@ -0,0 +1,7 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +
> +#include "coro.h"
> +
> +void bar () {
> +  co_await;  // { dg-error "expected primary-expression before" }
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C
> new file mode 100644
> index 0000000000..484859c706
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-01-outside-fn.C
> @@ -0,0 +1,5 @@
> +//  { 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} }
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C
> new file mode 100644
> index 0000000000..4ce5c2e04a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-02-outside-fn.C
> @@ -0,0 +1,5 @@
> +//  { 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} }
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
> new file mode 100644
> index 0000000000..7f4ed9afef
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-03-auto.C
> @@ -0,0 +1,16 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C
> new file mode 100644
> index 0000000000..ac0ba2e54f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-04-ctor-dtor.C
> @@ -0,0 +1,8 @@
> +//  { 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" }
> +};
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C
> new file mode 100644
> index 0000000000..73a0b1499d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-05-constexpr.C
> @@ -0,0 +1,12 @@
> +//  { 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 ();
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C
> new file mode 100644
> index 0000000000..ab520baaff
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-06-main.C
> @@ -0,0 +1,7 @@
> +//  { 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" }
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
> new file mode 100644
> index 0000000000..4e41dd3be4
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-07-varargs.C
> @@ -0,0 +1,14 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C
> new file mode 100644
> index 0000000000..61db5feed3
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-await-syntax-08-lambda-auto.C
> @@ -0,0 +1,19 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C
> new file mode 100644
> index 0000000000..3fcd8dd104
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-01-outside-fn.C
> @@ -0,0 +1,6 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +
> +#include "coro.h"
> +
> +co_return; // { dg-error {expected unqualified-id before 'co_return'} }
> +
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C
> new file mode 100644
> index 0000000000..cda36eb2a3
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-02-outside-fn.C
> @@ -0,0 +1,5 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +
> +#include "coro.h"
> +
> +auto f (co_return); // { dg-error {expected primary-expression before 'co_return'} }
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
> new file mode 100644
> index 0000000000..93a04dc459
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-03-auto.C
> @@ -0,0 +1,12 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C
> new file mode 100644
> index 0000000000..9396432e8b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-04-ctor-dtor.C
> @@ -0,0 +1,8 @@
> +//  { 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" }
> +};
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C
> new file mode 100644
> index 0000000000..69b109fb60
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-05-constexpr-fn.C
> @@ -0,0 +1,12 @@
> +//  { 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 ();
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C
> new file mode 100644
> index 0000000000..40d7e4e362
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-06-main.C
> @@ -0,0 +1,7 @@
> +//  { 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" }
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
> new file mode 100644
> index 0000000000..0aea17a1db
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-07-vararg.C
> @@ -0,0 +1,14 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
> new file mode 100644
> index 0000000000..4bfa41cd4a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-08-bad-return.C
> @@ -0,0 +1,43 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C
> new file mode 100644
> index 0000000000..8fe52361ba
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-return-syntax-09-lambda-auto.C
> @@ -0,0 +1,19 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C
> new file mode 100644
> index 0000000000..547f1a31c0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-00-needs-expr.C
> @@ -0,0 +1,7 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +
> +#include "coro.h"
> +
> +void foo () {
> +  co_yield;  // { dg-error "expected primary-expression before" }
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C
> new file mode 100644
> index 0000000000..30db0e963b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-01-outside-fn.C
> @@ -0,0 +1,6 @@
> +//  { 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} }
> +
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C
> new file mode 100644
> index 0000000000..71e119fbef
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-02-outside-fn.C
> @@ -0,0 +1,6 @@
> +//  { dg-additional-options "-fsyntax-only -w" }
> +
> +#include "coro.h"
> +
> +int a[] = { co_yield 21 }; // { dg-error {'co_yield' cannot be used outside a function} }
> +
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
> new file mode 100644
> index 0000000000..808a07f5e1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-03-auto.C
> @@ -0,0 +1,12 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C
> new file mode 100644
> index 0000000000..cc46e01d77
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-04-ctor-dtor.C
> @@ -0,0 +1,8 @@
> +//  { 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" }
> +};
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C
> new file mode 100644
> index 0000000000..39ef19c63b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-05-constexpr.C
> @@ -0,0 +1,12 @@
> +//  { 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 ();
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C
> new file mode 100644
> index 0000000000..dcc3dbce75
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-06-main.C
> @@ -0,0 +1,7 @@
> +//  { 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" }
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
> new file mode 100644
> index 0000000000..f0b568335e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-07-varargs.C
> @@ -0,0 +1,14 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C
> new file mode 100644
> index 0000000000..86969f781e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-08-needs-expr.C
> @@ -0,0 +1,37 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C
> new file mode 100644
> index 0000000000..5190face00
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/co-yield-syntax-09-lambda-auto.C
> @@ -0,0 +1,19 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-builtins.C b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
> new file mode 100644
> index 0000000000..d7c4883384
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-builtins.C
> @@ -0,0 +1,17 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
> new file mode 100644
> index 0000000000..fb02e9d580
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-gro.C
> @@ -0,0 +1,32 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
> new file mode 100644
> index 0000000000..d489c3953a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-promise-yield.C
> @@ -0,0 +1,33 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
> new file mode 100644
> index 0000000000..f238c4b9a3
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-value.C
> @@ -0,0 +1,34 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
> new file mode 100644
> index 0000000000..c9f84e5902
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ret-void.C
> @@ -0,0 +1,34 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
> new file mode 100644
> index 0000000000..3943e78d9d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-1.C
> @@ -0,0 +1,17 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
> new file mode 100644
> index 0000000000..0f105c4c2d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-2.C
> @@ -0,0 +1,18 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
> new file mode 100644
> index 0000000000..d775d8a630
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh-3.C
> @@ -0,0 +1,18 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
> new file mode 100644
> index 0000000000..51e6135b8a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-missing-ueh.h
> @@ -0,0 +1,23 @@
> +#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
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
> new file mode 100644
> index 0000000000..f22a5e0833
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro-pre-proc.C
> @@ -0,0 +1,9 @@
> +// 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro.h b/gcc/testsuite/g++.dg/coroutines/coro.h
> new file mode 100644
> index 0000000000..ca12d2689e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro.h
> @@ -0,0 +1,151 @@
> +#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 *, ...);
> +extern "C" void abort (void) __attribute__((__noreturn__));
> +
> +#ifndef OUTPUT
> +#  define PRINT(X)
> +#  define PRINTF (void)
> +#else
> +#  define PRINT(X) puts(X)
> +#  define PRINTF printf
> +#endif
> diff --git a/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h b/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
> new file mode 100644
> index 0000000000..b961755e47
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coro1-ret-int-yield-int.h
> @@ -0,0 +1,133 @@
> +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;
> +  };
> +
> +};
> diff --git a/gcc/testsuite/g++.dg/coroutines/coroutines.exp b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
> new file mode 100644
> index 0000000000..e7fd4dac46
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/coroutines.exp
> @@ -0,0 +1,50 @@
> +#   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
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C b/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C
> new file mode 100644
> index 0000000000..8430d053c6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/alloc-00-gro-on-alloc-fail.C
> @@ -0,0 +1,118 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C b/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C
> new file mode 100644
> index 0000000000..f779f6e486
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/alloc-01-overload-newdel.C
> @@ -0,0 +1,120 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C b/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
> new file mode 100644
> index 0000000000..ee108072f6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-00-co-aw-arg.C
> @@ -0,0 +1,73 @@
> +// { 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C
> new file mode 100644
> index 0000000000..0f5785163f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-01-multiple-co-aw.C
> @@ -0,0 +1,73 @@
> +// { 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
> new file mode 100644
> index 0000000000..4982c49d79
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-02-temp-co-aw.C
> @@ -0,0 +1,72 @@
> +// { 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C
> new file mode 100644
> index 0000000000..d0bb4667ac
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/call-03-temp-ref-co-aw.C
> @@ -0,0 +1,72 @@
> +// { 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
> new file mode 100644
> index 0000000000..932fe4b283
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-00-co-ret.C
> @@ -0,0 +1,41 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C
> new file mode 100644
> index 0000000000..0bd477044b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-01-co-ret-parm.C
> @@ -0,0 +1,57 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C
> new file mode 100644
> index 0000000000..0cc6069c32
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-02-templ-parm.C
> @@ -0,0 +1,52 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C
> new file mode 100644
> index 0000000000..2d888a7455
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-03-operator-templ-parm.C
> @@ -0,0 +1,52 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C b/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
> new file mode 100644
> index 0000000000..e191c20ac0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-04-lambda-1.C
> @@ -0,0 +1,58 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C b/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C
> new file mode 100644
> index 0000000000..968940f505
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-05-lambda-capture-copy-local.C
> @@ -0,0 +1,59 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C
> new file mode 100644
> index 0000000000..db60132b0e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/class-06-lambda-capture-ref.C
> @@ -0,0 +1,59 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C
> new file mode 100644
> index 0000000000..a24c261599
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-00-trivial.C
> @@ -0,0 +1,52 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C
> new file mode 100644
> index 0000000000..db5c90224d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-01-with-value.C
> @@ -0,0 +1,57 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
> new file mode 100644
> index 0000000000..79ee6e1714
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-02-xform.C
> @@ -0,0 +1,58 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
> new file mode 100644
> index 0000000000..6408432573
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-03-rhs-op.C
> @@ -0,0 +1,58 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C
> new file mode 100644
> index 0000000000..9bc99e875d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-04-control-flow.C
> @@ -0,0 +1,50 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
> new file mode 100644
> index 0000000000..34af740c99
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-05-loop.C
> @@ -0,0 +1,51 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
> new file mode 100644
> index 0000000000..14945faffd
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-06-ovl.C
> @@ -0,0 +1,65 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
> new file mode 100644
> index 0000000000..33f8e99d8c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-07-tmpl.C
> @@ -0,0 +1,132 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C
> new file mode 100644
> index 0000000000..d34619d6b6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-08-cascade.C
> @@ -0,0 +1,63 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
> new file mode 100644
> index 0000000000..525c6fc467
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-09-pair.C
> @@ -0,0 +1,57 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C
> new file mode 100644
> index 0000000000..71a5b18c3c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-10-template-fn-arg.C
> @@ -0,0 +1,60 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C
> new file mode 100644
> index 0000000000..78c88ed14e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-11-forwarding.C
> @@ -0,0 +1,43 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C
> new file mode 100644
> index 0000000000..189332b78e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-12-operator-2.C
> @@ -0,0 +1,66 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C
> new file mode 100644
> index 0000000000..339ebe4ff2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-await-13-return-ref.C
> @@ -0,0 +1,58 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C
> new file mode 100644
> index 0000000000..f551c6e760
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-00-void-return-is-ready.C
> @@ -0,0 +1,90 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C
> new file mode 100644
> index 0000000000..03fc6eeb84
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-01-void-return-is-suspend.C
> @@ -0,0 +1,94 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C
> new file mode 100644
> index 0000000000..36da680f7f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-03-different-GRO-type.C
> @@ -0,0 +1,92 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C
> new file mode 100644
> index 0000000000..29fb9424f8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-04-GRO-nontriv.C
> @@ -0,0 +1,109 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C
> new file mode 100644
> index 0000000000..42b80ff6bb
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-05-return-value.C
> @@ -0,0 +1,38 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C
> new file mode 100644
> index 0000000000..5b1acb8145
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-06-template-promise-val-1.C
> @@ -0,0 +1,105 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C
> new file mode 100644
> index 0000000000..b1a06f2849
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-07-void-cast-expr.C
> @@ -0,0 +1,44 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C
> new file mode 100644
> index 0000000000..266bc7b3b2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-08-template-cast-ret.C
> @@ -0,0 +1,104 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C
> new file mode 100644
> index 0000000000..91f3f14cc0
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-09-bool-await-susp.C
> @@ -0,0 +1,97 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C
> new file mode 100644
> index 0000000000..7b07be5f44
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-10-expression-evaluates-once.C
> @@ -0,0 +1,49 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C
> new file mode 100644
> index 0000000000..06939107d8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-11-co-ret-co-await.C
> @@ -0,0 +1,40 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C
> new file mode 100644
> index 0000000000..50124c080b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-12-co-ret-fun-co-await.C
> @@ -0,0 +1,48 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C
> new file mode 100644
> index 0000000000..9d4a4de8eb
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-13-template-2.C
> @@ -0,0 +1,56 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C
> new file mode 100644
> index 0000000000..ebc1adba82
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-ret-14-template-3.C
> @@ -0,0 +1,58 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C
> new file mode 100644
> index 0000000000..586b6b2571
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-00-triv.C
> @@ -0,0 +1,129 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C
> new file mode 100644
> index 0000000000..5df69c7f15
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-01-multi.C
> @@ -0,0 +1,64 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C
> new file mode 100644
> index 0000000000..8d4f1d5d82
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-02-loop.C
> @@ -0,0 +1,68 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C
> new file mode 100644
> index 0000000000..cceee1f19e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl.C
> @@ -0,0 +1,140 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C
> new file mode 100644
> index 0000000000..d9330b33b7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-04-complex-local-state.C
> @@ -0,0 +1,162 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C
> new file mode 100644
> index 0000000000..043f97b6e1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-05-co-aw.C
> @@ -0,0 +1,55 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C
> new file mode 100644
> index 0000000000..c74e44d15d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-06-fun-parm.C
> @@ -0,0 +1,64 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C
> new file mode 100644
> index 0000000000..74dae63395
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-07-template-fn-param.C
> @@ -0,0 +1,71 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C
> new file mode 100644
> index 0000000000..8e39127a1a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-08-more-refs.C
> @@ -0,0 +1,68 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C
> new file mode 100644
> index 0000000000..3abbe1c43a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/co-yield-09-more-templ-refs.C
> @@ -0,0 +1,68 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
> new file mode 100644
> index 0000000000..d2463b2798
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/coro-torture.exp
> @@ -0,0 +1,19 @@
> +# 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
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
> new file mode 100644
> index 0000000000..164c804797
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/exceptions-test-0.C
> @@ -0,0 +1,167 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C
> new file mode 100644
> index 0000000000..b5716972d4
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-00.C
> @@ -0,0 +1,42 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C
> new file mode 100644
> index 0000000000..f530431a6b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-01.C
> @@ -0,0 +1,45 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C
> new file mode 100644
> index 0000000000..396b438cb2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-02.C
> @@ -0,0 +1,50 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C
> new file mode 100644
> index 0000000000..bf699722a1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-03.C
> @@ -0,0 +1,49 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C
> new file mode 100644
> index 0000000000..789e2c05b6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-04.C
> @@ -0,0 +1,57 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C
> new file mode 100644
> index 0000000000..8bdb2b5d0f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-05.C
> @@ -0,0 +1,57 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C b/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C
> new file mode 100644
> index 0000000000..cbcfe67ff1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/func-params-06.C
> @@ -0,0 +1,47 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C
> new file mode 100644
> index 0000000000..61e284d5c8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-00-co-ret.C
> @@ -0,0 +1,35 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C
> new file mode 100644
> index 0000000000..378eedc6d8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-01-co-ret-parm.C
> @@ -0,0 +1,48 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C
> new file mode 100644
> index 0000000000..a6f592cd77
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-02-co-yield-values.C
> @@ -0,0 +1,64 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C
> new file mode 100644
> index 0000000000..bfa5400225
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-03-auto-parm-1.C
> @@ -0,0 +1,46 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C
> new file mode 100644
> index 0000000000..adf31e22db
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-04-templ-parm.C
> @@ -0,0 +1,47 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C
> new file mode 100644
> index 0000000000..7cd6648cca
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-05-capture-copy-local.C
> @@ -0,0 +1,66 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C
> new file mode 100644
> index 0000000000..7b445d3d9c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-06-multi-capture.C
> @@ -0,0 +1,48 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C
> new file mode 100644
> index 0000000000..2bd58cbf2e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-07-multi-yield.C
> @@ -0,0 +1,46 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C b/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C
> new file mode 100644
> index 0000000000..4d5a44fe29
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/lambda-08-co-ret-parm-ref.C
> @@ -0,0 +1,59 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
> new file mode 100644
> index 0000000000..a8956457dc
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-0.C
> @@ -0,0 +1,37 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
> new file mode 100644
> index 0000000000..69a5b70756
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-1.C
> @@ -0,0 +1,37 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
> new file mode 100644
> index 0000000000..f232edabda
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-2.C
> @@ -0,0 +1,50 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
> new file mode 100644
> index 0000000000..bd06db53d4
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-3.C
> @@ -0,0 +1,65 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
> new file mode 100644
> index 0000000000..419eb6b646
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/local-var-4.C
> @@ -0,0 +1,75 @@
> +//  { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
> new file mode 100644
> index 0000000000..934fb19de7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/mid-suspend-destruction-0.C
> @@ -0,0 +1,107 @@
> +// { 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;
> +}
> diff --git a/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C b/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C
> new file mode 100644
> index 0000000000..b2f1be78b3
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/coroutines/torture/pr92933.C
> @@ -0,0 +1,18 @@
> +//  { 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) {
> +    }
> +}
> --
> 2.14.3

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 5/7, v2] Standard library header.
  2020-01-09 12:39             ` [C++ coroutines 5/7, v2] " Iain Sandoe
@ 2020-01-09 13:50               ` Jonathan Wakely
  2020-01-09 19:57                 ` [C++ coroutines 5/7, v3] " Iain Sandoe
  0 siblings, 1 reply; 30+ messages in thread
From: Jonathan Wakely @ 2020-01-09 13:50 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches, libstdc++

On 09/01/20 12:39 +0000, Iain Sandoe wrote:
>Hi Jonathan
>
>The SVN commit IDs that relate to the amendments are noted in the
>revised patch header below.
>
>Jonathan Wakely <jwakely@redhat.com> wrote:
>
>> On 17/11/19 10:27 +0000, Iain Sandoe wrote:
>
>>> 	* include/Makefile.in: Regnerated.
>>
>> "Regnerated" typo.
>Fixed,
>
>>> 	${experimental_srcdir}/chrono \
>>> +	${experimental_srcdir}/coroutine \
>>
>> The experimental dir is (currently) only used for TS headers. All
>> C++20 support is currently experimental, so adding <coroutines> where
>> <concepts> and <ranges> have been added would be OK.
>>
>> But I'm not really clear if this is an implementation of the TS or the
>> C++20 feature.  If it's a hybrid, putting it in
>> <experimental/coroutines> is fine.
>
>Mea culpa;
>this began life as a TS and when it was included in the WD (and then CD)
>I failed to move the header and update.
>
>My intention is that the facility will work with at least C++14 as a GNU
>extension (as do the impls. in clang and EDG).  In fact, I can’t see any
>real reason that it wouldn’t work with C++11 - but time is too short for
>that experiment.

Life's too short to support C++11. GCC's default is C++14, so anybody
explicitly requesting C++11 should expect not to get shiny new toys.

It uses names that aren't reserved in C++14 and C++17, but as it's a
new header anybody explicitly including it in C++14 gets what they
deserve^W asked for. Another option would be to use:

#if __cplusplus > 201703L || (__cplusplus >= 201402L && !defined __STRICT_ANSI__)

I don't think we need to do that. We can revisit if people complain,
but I doubt they will.

>> When the final <coroutines> header is added it will need to be in
>> libsupc++ so that it's included for freestanding builds (and at that
>> point it won't be able to use <bits/stl_function.h>, but that will be
>> OK as the final header will be C++20-only and can rely on <compare>
>> unconditionally, which is also freestanding).
>
>Well, for the reason above, I have made this not depend on the availability
>of <compare> or spaceship.

Makes sense.

>>> +/** @file experimental/coroutine
>>> + *  This is an experimental C++ Library header against the C++20 CD n4835.
>>> + *  @ingroup coroutine-ts
>>
>> The coroutine-ts doc group should be defined somewhere.
>
>I’ve reworked the doxygen stuff somewhat, although freely admit that it’s currently
>rather weak as documentation.  I do expect some more editorial changes to the std
>and documentation could be improved before 10 releases.

That's fine. Our doxygen API docs need a lot of improvement anyway.

>>> +  namespace experimental {
>>> +  inline namespace coroutines_n4835 {
>>
>> This should be a reserved name too, e.g. __coroutines_n4835.
>done
>>>
>>> +    constexpr coroutine_handle () noexcept : __fr_ptr (0) {}
>>
>> The libstdc++ naming convention is _M_xxx for non-static members (both
>> data members and member functions) and _S_xxx for static members
>> (again, both data member and functions).
>>
>> This helps to distinguish members from other uglified names like
>> function parameters and local variables.
>done.
>
>Hopefully, this amended version is closer and the intent is clearer.
>OK for trunk now?
>thanks
>Iain
>
>———
>
>This provides the interfaces mandated by the standard and implements
>the interaction with the coroutine frame by means of inline use of
>builtins expanded at compile-time.  There should be a 1:1 correspondence
>with the standard sections which are cross-referenced.
>
>There is no runtime content.
>
>At this stage we have the content in an inline namespace "__n4835" for
>the current CD.
>
>Squashed commits:
>
>r278724 - Address review comments, GNU naming conventions
>r278778 - Address review comments, update doxygen group info.
>r279817 - Update copyright year.
>r279845 - Address review comments, move coroutine header to std.
>r280038 - Correct pathname in the edit header.
>
>libstdc++-v3/ChangeLog:
>
>2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>
>
>	* include/Makefile.am: Add coroutine to the std set.
>	* include/Makefile.in: Regenerated.
>	* include/std/coroutine: New file.
>---
> libstdc++-v3/include/Makefile.am   |   1 +
> libstdc++-v3/include/Makefile.in   |   1 +
> libstdc++-v3/include/std/coroutine | 269 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 271 insertions(+)
> create mode 100644 libstdc++-v3/include/std/coroutine
>
>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index b38defcafb..ad4404793b 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -38,6 +38,7 @@ std_headers = \
> 	${std_srcdir}/complex \
> 	${std_srcdir}/concepts \
> 	${std_srcdir}/condition_variable \
>+	${std_srcdir}/coroutine \
> 	${std_srcdir}/deque \
> 	${std_srcdir}/execution \
> 	${std_srcdir}/filesystem \
>diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
>index ae4a493ea6..f8b5645224 100644
>--- a/libstdc++-v3/include/Makefile.in
>+++ b/libstdc++-v3/include/Makefile.in
>@@ -382,6 +382,7 @@ std_headers = \
> 	${std_srcdir}/complex \
> 	${std_srcdir}/concepts \
> 	${std_srcdir}/condition_variable \
>+	${std_srcdir}/coroutine \
> 	${std_srcdir}/deque \
> 	${std_srcdir}/execution \
> 	${std_srcdir}/filesystem \
>diff --git a/libstdc++-v3/include/std/coroutine b/libstdc++-v3/include/std/coroutine
>new file mode 100644
>index 0000000000..9eaffaba12
>--- /dev/null
>+++ b/libstdc++-v3/include/std/coroutine
>@@ -0,0 +1,269 @@
>+// <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_EXPERIMENTAL_COROUTINE
>+#define _GLIBCXX_EXPERIMENTAL_COROUTINE 1

Did you mean to leave EXPERIMENTAL in this macro?
(If you change it, don't forget the one on the last line of the file).

>+
>+#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>
>+#  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 _R, typename...> struct coroutine_traits

_R isn't in our list of identifiers to avoid, but it's uncomfortably
close to some of them. We generally try to avoid single-letter names.
Please use something like _Ret or _Res instead.

>+  {
>+    using promise_type = typename _R::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 (decltype (nullptr) __h) noexcept

std::nullptr_t is defined in <bits/c++config.h> so you could use that
here.

>+      : _M_fr_ptr (__h)
>+    {}

New line after this function body please.

>+    coroutine_handle &operator= (decltype (nullptr)) noexcept

Libstdc++ coding standards differ from the rest of GCC. We group the
ptr-declarator with the type, not the name, and there's no space
before the parens, so:

     coroutine_handle& operator=(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;
>+#else
>+  // These are from the TS 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 <class _Promise> struct coroutine_handle : coroutine_handle<>

s/class/typename/ in the template-head please.

>+  {
>+    // 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= (decltype (nullptr)) 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 <>
>+  class coroutine_handle<noop_coroutine_promise> : public coroutine_handle<>

The primary template was declared as a struct but this is a class. It
doesn't really matter, but let's keep it consistent to avoid annoying
-Wmismatched-tags warnings from Clang.

>+  {
>+    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
>+    // constexpr void* address() const noexcept;
>+  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_GLIBCXX_VISIBILITY(default)

No need for the _GLIBCXX... part here.

>+
>+#endif // C++14 (we are allowing use from at least this)
>+
>+#endif // _GLIBCXX_EXPERIMENTAL_COROUTINE
>-- 
>2.14.3
>
>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 4/7, v2] Middle end expanders and transforms.
  2020-01-09 12:38           ` [C++ coroutines 4/7, v2] " Iain Sandoe
@ 2020-01-09 14:38             ` Richard Biener
  0 siblings, 0 replies; 30+ messages in thread
From: Richard Biener @ 2020-01-09 14:38 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches, libstdc++

On Thu, Jan 9, 2020 at 1:38 PM Iain Sandoe <iain@sandoe.co.uk> wrote:
>
> Hi Richard,
>
> The SVN commit IDs that address changes to this part of the patchset are noted
> in the revised patch header below, for reference.
>
> Richard Biener <richard.guenther@gmail.com> wrote:
>
> > On Sun, Nov 17, 2019 at 11:27 AM Iain Sandoe <iain@sandoe.co.uk> wrote:
>
> >>   Once we have remade the connections to their correct postions, we elide
> >>   the labels that the front end inserted.
> >
> > Comments inline.
>
> > Do  you actually verify and error for non-INTEGER_CSTs in the
> > frontend?  Users can
> > inject malicous calls to builtins at their discretion.
>
> Done (it seems that there’s no generic support for such a constraint so
> I added a specific validation for this).
>
> >> +           bool dir = wi::to_wide (from) != 0;
> >> +           tree vptr = build_pointer_type (void_type_node);
> >
> > this is ptr_type_node (also elsewhere)
> done throughout.
>
> >> +                               build_int_cst (sizetype, offs));
> >
> > size_int  (offs)
> done
>
> >> +           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.  */
> >> +           tree vptr = build_pointer_type (void_type_node);
> >> +           HOST_WIDE_INT psize = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (vptr));
> >> +           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,
> >> +                                        wide_int_to_tree (fntype_ppp, offset));
> >
> > You are reading a pointer to the coro_resume builtin here?  You are careful
> > with using pointer-to-function types but the offset calculation uses the size
> > of void * here.
> > Note that the type of the offset operand in a MEM_REF denotes
> > TBAA info, so stores to whatever is at *ptr should better have matching types
> > (in this light re-using the function type of the builtin might be iffy).
>
> In fact, in general, the object passed to this builtin will be type-erased.  We will
> not know the true type of the “promise object” or the handle.  The only facts that
> we can rely on here are that the layout contains:
>
> ….. something…
> void (*)(void*) [resume]
> void (*)(void*) [destroy]
> promise: <= we only know that this is its start address, not the type.
> …. something….
>
> So we could only say at this stage for sure that the extent would be at least one
> byte.
>
> ** (note) However, we are never trying to manipulate the promise object at this
> stage, when ever we _are_ trying to manipulate it, we will have a complete frame
> type as part of the call.
>
> Having said that, perhaps I’m still missing a change that could improve this.
>
> > While wide_int_to_tree seems to match HOST_WIDE_INT the canonical
> > way across the code-base is to use build_int_cst (type, hwint).
> done.
>
> >> +           tree d_ptr_tmp = make_ssa_name (TYPE_MAIN_VARIANT (vptr));
> >
> > No need for TYPE_MAIN_VARIANT here if you use ptr_type_node.
> fixed.
> >
> >> +           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,
> >> +                                    wide_int_to_tree (vptr, 0));
> >
> > null_pointer_node
> fixed
>
> >> +  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);
> >
> > it would be nice to elide the function walk for functions not
> > containing any CO* stuff (you noted that below for other parts).
> > We can spend a bit in struct function noting functions with
> > coroutine code inside and set the bit from frontends or from
> > the gimplifier for example.  Since it's behind the flag_coroutines
> > paywall this can be addressed as followup.
>
> I did this anyway.
>
> >> +  basic_block bb;
> >
> > A comment on why we need two IL walks would be nice.  It looks like you
> > could have remembered CO_ACTOR calls in the first walk in a vec<gcall *>
> > for example and then just process that?
>
> I fixed this - with a worklist.
>
> >> +  if (changed)
> >> +    {
> >> +      /* 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)
> >
> > An extra walk over all BBs looks a bit excessive?  The label_to_block
> > map should be set up so you could walk the set and from there
> > affected blocks.
>
> I am concerned that we might need to remove labels for coroutine yield
> IFNs that have been removed already - and thus are not “seen” above.
> For now, I left this as is with the early exit you noted below.
> (I guess it would not be hard to make it a worklist for this too)
>
> >> +       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.  */
> >> +                 }
> >> +             }
> >> +           gsi_next (&gsi);
> >
> > At least you can stop walking a BB if you hit something else
> > than a label.  Thus, else break;
>
> done.
>
> >> +/* Optimize (not yet) and lower frame allocation.
> >> +
> >> +   This is a place-holder for an optimisation to remove unused frame
> >> +   entries and re-size the frame to minimum.  */
> >
> > I wonder if this will ever be reasonably possible at this point of the
> > compilation.  So instead of having this placeholder please do
> > the copy through during one of the lowering phases.
>
> I don’t want to side-track the patch discussion at this juncture:
>
>  a) I removed the pass.
>  b) and will just note that the coroutine frame is the direct analogue of the
>  activation record; it is entirely under the control of the compiler and each
>  entry could be considered as independent as any stack frame entry.  Thus
>  there cannot be any hidden aliassing. I’d like to discuss in some separate
>  thread the opportunities here (it seems to me to be an entirely different
>  problem from the general “reorganise aggregate layout” being discussed
>  elsewhere).

Well, it at least is related in the sense as the compiler might have chosen to
take the address of something (consider an array being part of that frame).
Or mangle the frame accesses in some other interesting way (store-merging).
So you cannot rely on all accesses being "nicely" in their original form.

Note the very same optimization would apply to nested function static chains.
IIRC for Ada we do (or there were patches to do this) try to tear the frame up
in SRA again after inlining a nested function into its caller.  Not sure whether
for coroutines we ever expose such situation.  But also with not inlining
the ABI is completely local for nested functions so we _could_ optimize
it "late" - but run into the very same complications.

> >>   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);
> >
> > So the two passes above are really close - what's the reason to not
> > do coroutine_lower_builtins when the CFG is already built?
>
> The first pass has to run for any function (since the library builtins could and
> will be used by non-coroutine functions - e.g. in libstdc++ headers).
>
> The first pass also lowers the IFN_CO_SUSP - which modifies the CFG.
>
> The second pass needs the CFG, actually it would be nice to shift that pass
> later in the pipeline to improve the chances of elision of CO_YIELD points via
> DCE.
>
> Revised patch below
> OK for thrunk now?

Yes.

Thanks,
Richard.

> thanks
> Iain
>
> =======
>
> As described in the covering note, the main part of this is the
> expansion of the library support builtins, these are simple boolean
> or numerical substitutions.  This pass has to run for non-coroutine
> functions, since any 'regular' function may make use of them.
>
> The functionality of implementing an exit from scope without cleanup
> is performed here by lowering an IFN to a gimple goto.
>
> The final part is the expansion of the coroutine IFNs that describe the
> state machine connections to the dispatchers.  This only has to be run
> for functions that are coroutine components.
>
>    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.
>
> Squashed commits.
>
> r278861 - Address review comments, use ptr_type_node
> r278864 - Address review comments, more standard nodes and APIs
> r278884 - Address review comments, remove co_frame lowering pass.
> r278936 - Address review comments, make a more specific pass gate.
> r279098 - Address review comments, exit early where possible.
> r279405 - Address review comments, more whitespace and comment changes.
> r279726 - Address review comments, remove one iteration through BBs.
> r279740 - Re-indent lower_coro_builtin.
> r279741 - Remove unnecessary control flow, re-indent.
> r279817 - Update copyright year.
> r280035 - Fix trailing whitespace.
>
> gcc/ChangeLog:
>
> 2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>
>
>         * Makefile.in: Add coroutine-passes.o.
>         * coroutine-passes.cc: New file.
>         * function.h (struct GTY function): Add a bit to indicate that the
>         function is a coroutine component.
>         * 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.
>
> gcc/cp/ChangeLog:
>
> 2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>
>
>         * decl.c (emit_coro_helper): Set a bit to signal that this is a
>         coroutine component.
>         (finish_function): Likewise.
> ---
>  gcc/Makefile.in         |   1 +
>  gcc/coroutine-passes.cc | 532 ++++++++++++++++++++++++++++++++++++++++++++++++
>  gcc/cp/decl.c           |   6 +
>  gcc/function.h          |   3 +
>  gcc/passes.def          |   2 +
>  gcc/tree-pass.h         |   2 +
>  6 files changed, 546 insertions(+)
>  create mode 100644 gcc/coroutine-passes.cc
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 61b512c9b7..0849ce4826 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1265,6 +1265,7 @@ OBJS = \
>         compare-elim.o \
>         context.o \
>         convert.o \
> +       coroutine-passes.o \
>         coverage.o \
>         cppbuiltin.o \
>         cppdefault.o \
> diff --git a/gcc/coroutine-passes.cc b/gcc/coroutine-passes.cc
> new file mode 100644
> index 0000000000..d032a392ce
> --- /dev/null
> +++ b/gcc/coroutine-passes.cc
> @@ -0,0 +1,532 @@
> +/* 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);
> +}
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 41c14001fd..6093613bae 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -16802,6 +16802,9 @@ emit_coro_helper (tree helper)
>    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);
>  }
> @@ -16858,6 +16861,9 @@ finish_function (bool inline_p)
>           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)),
> diff --git a/gcc/function.h b/gcc/function.h
> index 496d3f728c..1ee8ed3de5 100644
> --- a/gcc/function.h
> +++ b/gcc/function.h
> @@ -418,6 +418,9 @@ struct GTY(()) function {
>    /* 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.  */
> diff --git a/gcc/passes.def b/gcc/passes.def
> index b6bd4f3d31..2db4ef4acc 100644
> --- a/gcc/passes.def
> +++ b/gcc/passes.def
> @@ -39,8 +39,10 @@ along with GCC; see the file COPYING3.  If not see
>    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);
> diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
> index 5ff43572b8..f10f467462 100644
> --- a/gcc/tree-pass.h
> +++ b/gcc/tree-pass.h
> @@ -478,6 +478,8 @@ extern gimple_opt_pass *make_pass_gen_hsail (gcc::context *ctxt);
>  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);
> --
> 2.14.3

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 5/7, v3] Standard library header.
  2020-01-09 13:50               ` Jonathan Wakely
@ 2020-01-09 19:57                 ` Iain Sandoe
  2020-01-09 21:21                   ` Jonathan Wakely
  0 siblings, 1 reply; 30+ messages in thread
From: Iain Sandoe @ 2020-01-09 19:57 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: GCC Patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 1554 bytes --]

Hi Jonathan,

thanks for the review - hopefully the attached addresses both those
and the whitespace differences we discussed off-line.

I’m attaching the header as a textfile since it’s a new one, perhaps
that will make it easier to review the whitespace stuff.

thanks
Iain

Jonathan Wakely <jwakely@redhat.com> wrote:

> On 09/01/20 12:39 +0000, Iain Sandoe wrote:

>> +#ifndef _GLIBCXX_EXPERIMENTAL_COROUTINE
>> +#define _GLIBCXX_EXPERIMENTAL_COROUTINE 1
> 
> Did you mean to leave EXPERIMENTAL in this macro?
no, dropped.
>> 
>> +  template <typename _R, typename...> struct coroutine_traits
> 
> _R isn't in our list of identifiers to avoid, but it's uncomfortably
> close to some of them. We generally try to avoid single-letter names.
> Please use something like _Ret or _Res instead.

used “_Result".

>> \+    constexpr coroutine_handle (decltype (nullptr) __h) noexcept
> 
> std::nullptr_t is defined in <bits/c++config.h> so you could use that
> here.
done.

>> +    {}
> 
> New line after this function body please.

I’ve been through an added a newline after each body.

>> +    coroutine_handle &operator= (decltype (nullptr)) noexcept
> 
> Libstdc++ coding standards differ from the rest of GCC. We group the
> ptr-declarator with the type, not the name, and there's no space
> before the parens, so:
> 
>    coroutine_handle& operator=(nullptr_t) noexcept

Whitespace fixes applied throughout.

>> +} // namespace std_GLIBCXX_VISIBILITY(default)
> 
> No need for the _GLIBCXX... part here.

indeed, that was a typo.


[-- Attachment #2: coroutine.txt --]
[-- Type: text/plain, Size: 7392 bytes --]

// <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;
#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

[-- Attachment #3: Type: text/plain, Size: 9748 bytes --]


====
    
    This provides the interfaces mandated by the standard and implements
    the interaction with the coroutine frame by means of inline use of
    builtins expanded at compile-time.  There should be a 1:1 correspondence
    with the standard sections which are cross-referenced.
    
    There is no runtime content.
    
    At this stage we have the content in an inline namespace "__n4835" for
    the current CD.
    
    Squashed commits:
    
    r278724 - Address review comments, GNU naming conventions
    r278778 - Address review comments, update doxygen group info.
    r279817 - Update copyright year.
    r279845 - Address review comments, move coroutine header to std.
    r280038 - Correct pathname in the edit header.
    
    libstdc++-v3/ChangeLog:
    
    2020-01-09  Iain Sandoe  <iain@sandoe.co.uk>
    
            * include/Makefile.am: Add coroutine to the std set.
            * include/Makefile.in: Regenerated.
            * include/std/coroutine: New file.

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index b38defcafb..ad4404793b 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -38,6 +38,7 @@ std_headers = \
 	${std_srcdir}/complex \
 	${std_srcdir}/concepts \
 	${std_srcdir}/condition_variable \
+	${std_srcdir}/coroutine \
 	${std_srcdir}/deque \
 	${std_srcdir}/execution \
 	${std_srcdir}/filesystem \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index ae4a493ea6..f8b5645224 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -382,6 +382,7 @@ std_headers = \
 	${std_srcdir}/complex \
 	${std_srcdir}/concepts \
 	${std_srcdir}/condition_variable \
+	${std_srcdir}/coroutine \
 	${std_srcdir}/deque \
 	${std_srcdir}/execution \
 	${std_srcdir}/filesystem \
diff --git a/libstdc++-v3/include/std/coroutine b/libstdc++-v3/include/std/coroutine
new file mode 100644
index 0000000000..3c3c7d7f50
--- /dev/null
+++ b/libstdc++-v3/include/std/coroutine
@@ -0,0 +1,290 @@
+// <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;
+#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


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [C++ coroutines 5/7, v3] Standard library header.
  2020-01-09 19:57                 ` [C++ coroutines 5/7, v3] " Iain Sandoe
@ 2020-01-09 21:21                   ` Jonathan Wakely
  0 siblings, 0 replies; 30+ messages in thread
From: Jonathan Wakely @ 2020-01-09 21:21 UTC (permalink / raw)
  To: Iain Sandoe; +Cc: GCC Patches, libstdc++

On 09/01/20 19:43 +0000, Iain Sandoe wrote:
>Hi Jonathan,
>
>thanks for the review - hopefully the attached addresses both those
>and the whitespace differences we discussed off-line.
>
>I’m attaching the header as a textfile since it’s a new one, perhaps
>that will make it easier to review the whitespace stuff.
>
>thanks
>Iain
>
>Jonathan Wakely <jwakely@redhat.com> wrote:
>
>> On 09/01/20 12:39 +0000, Iain Sandoe wrote:
>
>>> +#ifndef _GLIBCXX_EXPERIMENTAL_COROUTINE
>>> +#define _GLIBCXX_EXPERIMENTAL_COROUTINE 1
>>
>> Did you mean to leave EXPERIMENTAL in this macro?
>no, dropped.
>>>
>>> +  template <typename _R, typename...> struct coroutine_traits
>>
>> _R isn't in our list of identifiers to avoid, but it's uncomfortably
>> close to some of them. We generally try to avoid single-letter names.
>> Please use something like _Ret or _Res instead.
>
>used “_Result".
>
>>> \+    constexpr coroutine_handle (decltype (nullptr) __h) noexcept
>>
>> std::nullptr_t is defined in <bits/c++config.h> so you could use that
>> here.
>done.
>
>>> +    {}
>>
>> New line after this function body please.
>
>I’ve been through an added a newline after each body.
>
>>> +    coroutine_handle &operator= (decltype (nullptr)) noexcept
>>
>> Libstdc++ coding standards differ from the rest of GCC. We group the
>> ptr-declarator with the type, not the name, and there's no space
>> before the parens, so:
>>
>>    coroutine_handle& operator=(nullptr_t) noexcept
>
>Whitespace fixes applied throughout.
>
>>> +} // namespace std_GLIBCXX_VISIBILITY(default)
>>
>> No need for the _GLIBCXX... part here.
>
>indeed, that was a typo.
>

>// <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;

I think this needs to be defined, not just declared:

   { return std::compare_three_way()(__a.address(), __b.address()); }


OK for trunk (assuming it still works with that change).


^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2020-01-09 20:20 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-17 10:23 [C++ coroutines 0/6] Implement C++ coroutines Iain Sandoe
2019-11-17 10:24 ` [C++ coroutines 1/6] Common code and base definitions Iain Sandoe
2019-11-17 10:24   ` [C++ coroutines 2/6] Define builtins and internal functions Iain Sandoe
2019-11-17 10:26     ` [C++ coroutines 3/6] Front end parsing and transforms Iain Sandoe
2019-11-17 10:26       ` [C++ coroutines 4/6] Middle end expanders " Iain Sandoe
2019-11-17 10:27         ` [C++ coroutines 5/6] Standard library header Iain Sandoe
2019-11-17 10:28           ` [C++ coroutines 6/6] Testsuite Iain Sandoe
2019-11-19 10:01             ` Richard Biener
2020-01-09 12:40               ` [C++ coroutines 6/7, v2] Testsuite Iain Sandoe
2020-01-09 13:00                 ` Iain Sandoe
2020-01-09 13:17                 ` Richard Biener
2019-11-20 11:13             ` [C++ coroutines 6/6] Testsuite JunMa
2019-11-20 11:22               ` Iain Sandoe
2019-11-20 13:11                 ` JunMa
2019-11-17 16:19           ` [C++ coroutines 5/6] Standard library header Jonathan Wakely
2020-01-09 12:39             ` [C++ coroutines 5/7, v2] " Iain Sandoe
2020-01-09 13:50               ` Jonathan Wakely
2020-01-09 19:57                 ` [C++ coroutines 5/7, v3] " Iain Sandoe
2020-01-09 21:21                   ` Jonathan Wakely
2019-11-19 10:00         ` [C++ coroutines 4/6] Middle end expanders and transforms Richard Biener
2020-01-09 12:38           ` [C++ coroutines 4/7, v2] " Iain Sandoe
2020-01-09 14:38             ` Richard Biener
2019-11-18 13:23       ` [C++ coroutines 3/6] Front end parsing " Nathan Sidwell
2019-11-19 18:40       ` Nathan Sidwell
2020-01-09 12:37         ` [C++ coroutines 3/7, v2] " Iain Sandoe
2019-11-17 16:54     ` [C++ coroutines 2/6] Define builtins and internal functions Jeff Law
2020-01-09 12:36       ` [C++ coroutines 2/7, v2] " Iain Sandoe
2019-11-17 15:49   ` [C++ coroutines 1/6] Common code and base definitions Jeff Law
2020-01-09 12:36     ` [C++ coroutines 1/7] " Iain Sandoe
2019-11-18 12:35 ` [C++ coroutines 0/6] Implement C++ coroutines Nathan Sidwell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).