[C++,coroutines,3/6] Front end parsing and transforms.
diff mbox series

Message ID EC0A5CB6-61E5-4F68-AB6C-CF8BACC7FFD7@sandoe.co.uk
State New
Headers show
Series
  • Implement C++ coroutines.
Related show

Commit Message

Iain Sandoe Nov. 17, 2019, 10:25 a.m. UTC
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

Comments

Nathan Sidwell Nov. 18, 2019, 1:23 p.m. UTC | #1
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 Nov. 19, 2019, 6:40 p.m. UTC | #2
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

Patch
diff mbox series

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