coroutines: Wrap co_await in a target expr where needed [PR95050]
[gcc.git] / gcc / cp / coroutines.cc
index 36229c77b4e7212332e27c889d62982c4d32ed45..e689625793239d6a11abe9a589262dcdb95bfe7f 100644 (file)
@@ -296,14 +296,26 @@ instantiate_coro_traits (tree fndecl, location_t kw)
      type.  */
 
   tree functyp = TREE_TYPE (fndecl);
+  tree arg = DECL_ARGUMENTS (fndecl);
+  bool lambda_p = LAMBDA_FUNCTION_P (fndecl);
   tree arg_node = TYPE_ARG_TYPES (functyp);
   tree argtypes = make_tree_vec (list_length (arg_node)-1);
   unsigned p = 0;
 
   while (arg_node != NULL_TREE && !VOID_TYPE_P (TREE_VALUE (arg_node)))
     {
-      TREE_VEC_ELT (argtypes, p++) = TREE_VALUE (arg_node);
+      /* See PR94807, as to why we must exclude lambda here.  */
+      if (is_this_parameter (arg) && !lambda_p)
+       {
+         /* We pass a reference to *this to the param preview.  */
+         tree ct = TREE_TYPE (TREE_TYPE (arg));
+         TREE_VEC_ELT (argtypes, p++) = cp_build_reference_type (ct, false);
+       }
+      else
+       TREE_VEC_ELT (argtypes, p++) = TREE_VALUE (arg_node);
+
       arg_node = TREE_CHAIN (arg_node);
+      arg = DECL_CHAIN (arg);
     }
 
   tree argtypepack = cxx_make_type (TYPE_ARGUMENT_PACK);
@@ -441,18 +453,6 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
     {
       /* Get the coroutine traits template class instance for the function
         signature we have - coroutine_traits <R, ...>  */
-      tree return_type = TREE_TYPE (TREE_TYPE (fndecl));
-      if (!CLASS_TYPE_P (return_type))
-       {
-         /* It makes more sense to show the function header for this, even
-            though we will have encountered it when processing a keyword.
-            Only emit the error once, not for every keyword we encounter.  */
-         if (!coro_info->coro_ret_type_error_emitted)
-           error_at (DECL_SOURCE_LOCATION (fndecl), "coroutine return type"
-                     " %qT is not a class", return_type);
-         coro_info->coro_ret_type_error_emitted = true;
-         return false;
-       }
 
       tree templ_class = instantiate_coro_traits (fndecl, loc);
 
@@ -696,9 +696,8 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind)
   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);
+                       NULL_TREE, NULL, tf_warning_or_error);
       /* If no viable functions are found, o is a.  */
       if (!o || o == error_mark_node)
        o = a;
@@ -748,7 +747,8 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind)
   if (INDIRECT_REF_P (e_proxy))
     e_proxy = TREE_OPERAND (e_proxy, 0);
   if (TREE_CODE (e_proxy) == PARM_DECL
-      || (TREE_CODE (e_proxy) == VAR_DECL && !DECL_ARTIFICIAL (e_proxy)))
+      || (VAR_P (e_proxy) && (!DECL_ARTIFICIAL (e_proxy)
+                             || DECL_HAS_VALUE_EXPR_P (e_proxy))))
     e_proxy = o;
   else
     {
@@ -816,6 +816,12 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind)
   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 te = NULL_TREE;
+  if (TREE_CODE (awrs_call) == TARGET_EXPR)
+    {
+      te = awrs_call;
+      awrs_call = TREE_OPERAND (awrs_call, 1);
+    }
   TREE_VEC_ELT (awaiter_calls, 2) = awrs_call; /* await_resume().  */
 
   tree await_expr = build5_loc (loc, CO_AWAIT_EXPR,
@@ -823,7 +829,13 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind)
                                a, e_proxy, o, awaiter_calls,
                                build_int_cst (integer_type_node,
                                               (int) suspend_kind));
-  return convert_from_reference (await_expr);
+  if (te)
+    {
+      TREE_OPERAND (te, 1) = await_expr;
+      await_expr = te;
+    }
+  tree t = convert_from_reference (await_expr);
+  return t;
 }
 
 tree
@@ -872,19 +884,18 @@ finish_co_await_expr (location_t kw, tree 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);
+                                NULL, tf_warning_or_error);
 
       /* As I read the section.
         We saw an await_transform method, so it's mandatory that we replace
         expr with p.await_transform (expr), therefore if the method call fails
         (presumably, we don't have suitable arguments) then this part of the
         process fails.  */
-      if (!at_fn || a == error_mark_node)
+      if (a == error_mark_node)
        return error_mark_node;
     }
 
@@ -944,40 +955,60 @@ finish_co_yield_expr (location_t kw, tree expr)
   if (!y_meth || y_meth == error_mark_node)
     return error_mark_node;
 
-  tree yield_fn = NULL_TREE;
+  /* [expr.yield] / 1
+     Let e be the operand of the yield-expression and p be an lvalue naming
+     the promise object of the enclosing coroutine, then the yield-expression
+     is equivalent to the expression co_await p.yield_value(e).
+     build p.yield_value(e):  */
   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;
+  tree yield_call = build_new_method_call
+    (get_coroutine_promise_proxy (current_function_decl), y_meth, &args,
+     NULL_TREE, LOOKUP_NORMAL, NULL, tf_warning_or_error);
 
-  /* So now we have the type of p.yield_value (e).
-     Now we want to build co_await p.yield_value (e).
+  /* Now build co_await p.yield_value (e).
      Noting that for co_yield, there is no evaluation of any potential
-     promise transform_await().  */
+     promise transform_await(), so we call build_co_await directly.  */
 
   tree op = build_co_await (kw, yield_call, CO_YIELD_SUSPEND_POINT);
   if (op != error_mark_node)
     {
-      op = build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (op), expr, op);
+      if (REFERENCE_REF_P (op))
+       op = TREE_OPERAND (op, 0);
+      /* If the await expression is wrapped in a TARGET_EXPR, then transfer
+        that wrapper to the CO_YIELD_EXPR, since this is just a proxy for
+        its contained await.  Otherwise, just build the CO_YIELD_EXPR.  */
+      if (TREE_CODE (op) == TARGET_EXPR)
+       {
+         tree t = TREE_OPERAND (op, 1);
+         t = build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (t), expr, t);
+         TREE_OPERAND (op, 1) = t;
+       }
+      else
+       op = build2_loc (kw, CO_YIELD_EXPR, TREE_TYPE (op), expr, op);
       TREE_SIDE_EFFECTS (op) = 1;
+      op = convert_from_reference (op);
     }
 
   return op;
 }
 
-/* Check that it's valid to have a co_return keyword here.
+/* Check and build a co_return statememt.
+   First 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.  */
+   These are built against a proxy for the promise, which will be filled
+   in with the actual frame version when the function is transformed.  */
 
 tree
 finish_co_return_stmt (location_t kw, tree expr)
 {
-  if (expr == error_mark_node)
+  if (expr)
+    STRIP_ANY_LOCATION_WRAPPER (expr);
+
+  if (error_operand_p (expr))
     return error_mark_node;
 
+  /* If it fails the following test, the function is not permitted to be a
+     coroutine, so the co_return statement is erroneous.  */
   if (!coro_common_keyword_context_valid_p (current_function_decl, kw,
                                            "co_return"))
     return error_mark_node;
@@ -986,32 +1017,32 @@ finish_co_return_stmt (location_t kw, tree expr)
      already.  */
   DECL_COROUTINE_P (current_function_decl) = 1;
 
-  if (processing_template_decl)
-    {
-      current_function_returns_value = 1;
+  /* This function will appear to have no return statement, even if it
+     is declared to return non-void (most likely).  This is correct - we
+     synthesize the return for the ramp in the compiler.  So suppress any
+     extraneous warnings during substitution.  */
+  TREE_NO_WARNING (current_function_decl) = true;
 
-      if (check_for_bare_parameter_packs (expr))
-       return error_mark_node;
+  if (processing_template_decl
+      && check_for_bare_parameter_packs (expr))
+    return error_mark_node;
 
-      tree functype = TREE_TYPE (current_function_decl);
-      /* If we don't know the promise type, we can't proceed, return the
-        expression as it is.  */
-      if (dependent_type_p (functype) || type_dependent_expression_p (expr))
-       {
-         expr
-           = build2_loc (kw, CO_RETURN_EXPR, void_type_node, expr, NULL_TREE);
-         expr = maybe_cleanup_point_expr_void (expr);
-         expr = add_stmt (expr);
-         return expr;
-       }
+  /* If we don't know the promise type, we can't proceed, build the
+     co_return with the expression unchanged.  */
+  tree functype = TREE_TYPE (current_function_decl);
+  if (dependent_type_p (functype) || type_dependent_expression_p (expr))
+    {
+      /* co_return expressions are always void type, regardless of the
+        expression type.  */
+      expr = build2_loc (kw, CO_RETURN_EXPR, void_type_node,
+                        expr, NULL_TREE);
+      expr = maybe_cleanup_point_expr_void (expr);
+      return add_stmt (expr);
     }
 
   if (!coro_promise_type_found_p (current_function_decl, kw))
     return error_mark_node;
 
-  if (error_operand_p (expr))
-    return error_mark_node;
-
   /* Suppress -Wreturn-type for co_return, we need to check indirectly
      whether the promise type has a suitable return_void/return_value.  */
   TREE_NO_WARNING (current_function_decl) = true;
@@ -1019,16 +1050,29 @@ finish_co_return_stmt (location_t kw, tree expr)
   if (!processing_template_decl && warn_sequence_point)
     verify_sequence_points (expr);
 
+  if (expr)
+    {
+      /* If we had an id-expression obfuscated by force_paren_expr, we need
+        to undo it so we can try to treat it as an rvalue below.  */
+      expr = maybe_undo_parenthesized_ref (expr);
+
+      if (processing_template_decl)
+       expr = build_non_dependent_expr (expr);
+
+      if (error_operand_p (expr))
+       return error_mark_node;
+    }
+
   /* 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;
+  tree co_ret_call = error_mark_node;
   if (expr == NULL_TREE || VOID_TYPE_P (TREE_TYPE (expr)))
     {
       tree crv_meth
        = lookup_promise_method (current_function_decl,
                                 coro_return_void_identifier, kw,
                                 /*musthave=*/true);
-      if (!crv_meth || crv_meth == error_mark_node)
+      if (crv_meth == error_mark_node)
        return error_mark_node;
 
       co_ret_call = build_new_method_call (
@@ -1041,13 +1085,37 @@ finish_co_return_stmt (location_t kw, tree expr)
        = lookup_promise_method (current_function_decl,
                                 coro_return_value_identifier, kw,
                                 /*musthave=*/true);
-      if (!crv_meth || crv_meth == error_mark_node)
+      if (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);
+      /* [class.copy.elision] / 3.
+        An implicitly movable entity is a variable of automatic storage
+        duration that is either a non-volatile object or an rvalue reference
+        to a non-volatile object type.  For such objects in the context of
+        the co_return, the overload resolution should be carried out first
+        treating the object as an rvalue, if that fails, then we fall back
+        to regular overload resolution.  */
+
+      if (treat_lvalue_as_rvalue_p (expr, /*parm_ok*/true)
+         && CLASS_TYPE_P (TREE_TYPE (expr))
+         && !TYPE_VOLATILE (TREE_TYPE (expr)))
+       {
+         vec<tree, va_gc> *args = make_tree_vector_single (move (expr));
+         /* It's OK if this fails... */
+         co_ret_call = build_new_method_call
+           (get_coroutine_promise_proxy (current_function_decl), crv_meth,
+            &args, NULL_TREE, LOOKUP_NORMAL|LOOKUP_PREFER_RVALUE,
+            NULL, tf_none);
+       }
+
+      if (co_ret_call == error_mark_node)
+       {
+         vec<tree, va_gc> *args = make_tree_vector_single (expr);
+         /* ... but this must succeed if we didn't get the move variant.  */
+         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. */
@@ -1056,13 +1124,9 @@ finish_co_return_stmt (location_t kw, tree expr)
                "function declared %<noreturn%> has a"
                " %<co_return%> statement");
 
-  if (!co_ret_call || co_ret_call == error_mark_node)
-    return error_mark_node;
-
   expr = build2_loc (kw, CO_RETURN_EXPR, void_type_node, expr, co_ret_call);
   expr = maybe_cleanup_point_expr_void (expr);
-  expr = add_stmt (expr);
-  return expr;
+  return add_stmt (expr);
 }
 
 /* We need to validate the arguments to __builtin_coro_promise, since the
@@ -1742,7 +1806,9 @@ transform_await_wrapper (tree *stmt, int *do_subtree, void *d)
       && DECL_CONTEXT (*stmt) != xform->actor_fn)
     DECL_CONTEXT (*stmt) = xform->actor_fn;
 
-  if (TREE_CODE (*stmt) != CO_AWAIT_EXPR && TREE_CODE (*stmt) != CO_YIELD_EXPR)
+  /* We should have already lowered co_yields to their co_await.  */
+  gcc_checking_assert (TREE_CODE (*stmt) != CO_YIELD_EXPR);
+  if (TREE_CODE (*stmt) != CO_AWAIT_EXPR)
     return NULL_TREE;
 
   tree await_expr = *stmt;
@@ -1762,10 +1828,10 @@ struct param_info
   tree frame_type;   /* The type used to represent this parm in the frame.  */
   tree orig_type;    /* The original type of the parm (not as passed).  */
   bool by_ref;       /* Was passed by reference.  */
-  bool rv_ref;       /* Was an rvalue reference.  */
   bool pt_ref;       /* Was a pointer to object.  */
   bool trivial_dtor; /* The frame type has a trivial DTOR.  */
   bool this_ptr;     /* Is 'this' */
+  bool lambda_cobj;  /* Lambda capture object */
 };
 
 struct local_var_info
@@ -1812,14 +1878,6 @@ transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
          /* 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);
-
        /* For capture proxies, this could include the decl value expr.  */
        if (local_var.is_lambda_capture || local_var.has_value_expr_p)
          {
@@ -1841,6 +1899,22 @@ transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
                                     lvd->actor_frame, fld_ref, NULL_TREE);
          local_var.field_idx = fld_idx;
        }
+      /* FIXME: we should be able to do this in the loop above, but (at least
+        for range for) there are cases where the DECL_INITIAL contains
+        forward references.
+        So, now we've built the revised var in the frame, substitute uses of
+        it in initializers and the bind expr body.  */
+      for (lvar = BIND_EXPR_VARS (*stmt); lvar != NULL;
+          lvar = DECL_CHAIN (lvar))
+       {
+         /* 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);
+       }
       cp_walk_tree (&BIND_EXPR_BODY (*stmt), transform_local_var_uses, d, NULL);
 
       /* Now we have processed and removed references to the original vars,
@@ -1961,8 +2035,6 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   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;
@@ -2025,12 +2097,6 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
          if (parm.pt_ref)
            fld_idx = build1_loc (loc, CONVERT_EXPR, TREE_TYPE (arg), fld_idx);
 
-         /* We expect an rvalue ref. here.  */
-         if (parm.rv_ref)
-           fld_idx = convert_to_reference (DECL_ARG_TYPE (arg), fld_idx,
-                                           CONV_STATIC, LOOKUP_NORMAL,
-                                           NULL_TREE, tf_warning_or_error);
-
          int i;
          tree *puse;
          FOR_EACH_VEC_ELT (*parm.body_uses, i, puse)
@@ -2172,11 +2238,11 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   /* Expand co_returns in the saved function body  */
   fnbody = expand_co_returns (&fnbody, promise_proxy, ap, fs_label);
 
-  /* n4849 adds specific behaviour to treat exceptions thrown by the
-     await_resume () of the initial suspend expression.  In order to
-     implement this, we need to treat the initial_suspend expression
-     as if it were part of the user-authored function body.  This
-     only applies if exceptions are enabled.  */
+  /* Specific behaviour to treat exceptions thrown by the await_resume ()
+     of the initial suspend expression.  In order to implement this, we
+     need to treat the initial_suspend expression as if it were part of the
+     user-authored function body.  This only applies if exceptions are
+     enabled.  */
   if (flag_exceptions)
     {
       tree outer = fnbody;
@@ -2278,7 +2344,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
        }
     }
 
-  /* n4849 [dcl.fct.def.coroutine] / 12
+  /* [dcl.fct.def.coroutine] / 12
      The deallocation function’s name is looked up in the scope of the promise
      type.  If this lookup fails, the deallocation function’s name is looked up
      in the global scope.  If deallocation function lookup finds both a usual
@@ -2413,8 +2479,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
                       continue_label, continuation, 2};
   cp_walk_tree (&actor_body, await_statement_expander, &data, NULL);
 
-  actor_body = pop_stmt_list (actor_body);
-  BIND_EXPR_BODY (actor_bind) = actor_body;
+  BIND_EXPR_BODY (actor_bind) = pop_stmt_list (actor_body);
+  TREE_SIDE_EFFECTS (actor_bind) = true;
 
   finish_compound_stmt (stmt);
   DECL_SAVED_TREE (actor) = pop_stmt_list (actor_outer);
@@ -2602,9 +2668,12 @@ struct susp_frame_data
 static tree
 captures_temporary (tree *stmt, int *do_subtree, void *d)
 {
+  /* We should have already lowered co_yields to their co_await.  */
+  gcc_checking_assert (TREE_CODE (*stmt) != CO_YIELD_EXPR);
+
   /* Stop recursing if we see an await expression, the subtrees
      of that will be handled when it is processed.  */
-  if (TREE_CODE (*stmt) == CO_AWAIT_EXPR || TREE_CODE (*stmt) == CO_YIELD_EXPR)
+  if (TREE_CODE (*stmt) == CO_AWAIT_EXPR)
     {
       *do_subtree = 0;
       return NULL_TREE;
@@ -2658,7 +2727,8 @@ captures_temporary (tree *stmt, int *do_subtree, void *d)
        }
 
       /* This isn't a temporary.  */
-      if ((TREE_CODE (parm) == VAR_DECL && !DECL_ARTIFICIAL (parm))
+      if ((VAR_P (parm)
+          && (!DECL_ARTIFICIAL (parm) || DECL_HAS_VALUE_EXPR_P (parm)))
          || TREE_CODE (parm) == PARM_DECL
          || TREE_CODE (parm) == NON_LVALUE_EXPR)
        continue;
@@ -2721,17 +2791,14 @@ register_awaits (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
 {
   susp_frame_data *data = (susp_frame_data *) d;
 
-  if (TREE_CODE (*stmt) != CO_AWAIT_EXPR && TREE_CODE (*stmt) != CO_YIELD_EXPR)
+  /* We should have already lowered co_yields to their co_await.  */
+  gcc_checking_assert (TREE_CODE (*stmt) != CO_YIELD_EXPR);
+
+  if (TREE_CODE (*stmt) != CO_AWAIT_EXPR)
     return NULL_TREE;
 
   tree aw_expr = *stmt;
   location_t aw_loc = EXPR_LOCATION (aw_expr); /* location of the co_xxxx.  */
-  /* co_yield is syntactic sugar, re-write it to co_await.  */
-  if (TREE_CODE (aw_expr) == CO_YIELD_EXPR)
-    {
-      aw_expr = TREE_OPERAND (aw_expr, 1);
-      *stmt = aw_expr;
-    }
 
   /* If the awaitable is a parm or a local variable, then we already have
      a frame copy, so don't make a new one.  */
@@ -2741,7 +2808,8 @@ register_awaits (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
   if (INDIRECT_REF_P (aw))
     aw = TREE_OPERAND (aw, 0);
   if (TREE_CODE (aw) == PARM_DECL
-      || (TREE_CODE (aw) == VAR_DECL && !DECL_ARTIFICIAL (aw)))
+      || (VAR_P (aw) && (!DECL_ARTIFICIAL (aw)
+                        || DECL_HAS_VALUE_EXPR_P (aw))))
     ; /* Don't make an additional copy.  */
   else
     {
@@ -2753,6 +2821,17 @@ register_awaits (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d)
       free (nam);
     }
 
+  tree o = TREE_OPERAND (aw_expr, 2); /* Initialiser for the frame var.  */
+  /* If this is a target expression, then we need to remake it to strip off
+     any extra cleanups added.  */
+  if (TREE_CODE (o) == TARGET_EXPR)
+    TREE_OPERAND (aw_expr, 2) = get_target_expr (TREE_OPERAND (o, 1));
+
+  tree v = TREE_OPERAND (aw_expr, 3);
+  o = TREE_VEC_ELT (v, 1);
+  if (TREE_CODE (o) == TARGET_EXPR)
+    TREE_VEC_ELT (v, 1) = get_target_expr (TREE_OPERAND (o, 1));
+
   register_await_info (aw_expr, aw_field_type, aw_field_nam);
 
   /* Count how many awaits the current expression contains.  */
@@ -2866,6 +2945,7 @@ replace_statement_captures (tree *stmt, void *d)
        }
     }
   BIND_EXPR_BLOCK (aw_bind) = b_block;
+  TREE_SIDE_EFFECTS (aw_bind) = TREE_SIDE_EFFECTS (BIND_EXPR_BODY (aw_bind));
   *stmt = aw_bind;
 }
 
@@ -3515,17 +3595,24 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 {
   gcc_checking_assert (orig && TREE_CODE (orig) == FUNCTION_DECL);
 
+  *resumer = error_mark_node;
+  *destroyer = error_mark_node;
   if (!coro_function_valid_p (orig))
-    return false;
-
-  /* The ramp function does return a value.  */
-  current_function_returns_value = 1;
+    {
+      /* For early errors, we do not want a diagnostic about the missing
+        ramp return value, since the user cannot fix this - a 'return' is
+        not allowed in a coroutine.  */
+      TREE_NO_WARNING (orig) = true;
+      /* Discard the body, we can't process it further.  */
+      pop_stmt_list (DECL_SAVED_TREE (orig));
+      DECL_SAVED_TREE (orig) = push_stmt_list ();
+      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;
+  gcc_checking_assert (fnbody != NULL_TREE);
 
   /* 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).
@@ -3539,7 +3626,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   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);
+      append_to_statement_list (fnbody, &DECL_SAVED_TREE (orig));
+      /* Suppress warnings about the missing return value.  */
+      TREE_NO_WARNING (orig) = true;
       return false;
     }
 
@@ -3582,10 +3671,13 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
        {
          tree tlist = NULL_TREE;
          append_to_statement_list_force (fnbody, &tlist);
+         TREE_SIDE_EFFECTS (tlist) = TREE_SIDE_EFFECTS (fnbody);
          BIND_EXPR_BODY (update_body) = tlist;
        }
       tree new_body_list = NULL_TREE;
-      append_to_statement_list_force (update_body, &new_body_list);
+      TREE_SIDE_EFFECTS (update_body) = true;
+      append_to_statement_list (update_body, &new_body_list);
+      TREE_SIDE_EFFECTS (new_body_list) = true;
       fnbody = new_body_list;
     }
 
@@ -3593,7 +3685,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
      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);
 
@@ -3609,13 +3700,21 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
      the coroutine promise.  */
   tree initial_await = build_init_or_final_await (fn_start, false);
   if (initial_await == error_mark_node)
-    return false;
+    {
+      /* Suppress warnings about the missing return value.  */
+      TREE_NO_WARNING (orig) = true;
+      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;
+    {
+      /* Suppress warnings about the missing return value.  */
+      TREE_NO_WARNING (orig) = true;
+      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));
@@ -3670,6 +3769,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
          The second two entries start out empty - and only get populated
          when we see uses.  */
       param_uses = new hash_map<tree, param_info>;
+      bool lambda_p = LAMBDA_FUNCTION_P (orig);
 
       unsigned no_name_parm = 0;
       for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
@@ -3684,15 +3784,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
          if (actual_type == NULL_TREE)
            actual_type = error_mark_node;
          parm.orig_type = actual_type;
-         parm.by_ref = parm.rv_ref = parm.pt_ref = false;
-         if (TREE_CODE (actual_type) == REFERENCE_TYPE
-             && TYPE_REF_IS_RVALUE (DECL_ARG_TYPE (arg)))
-           {
-             parm.rv_ref = true;
-             actual_type = TREE_TYPE (actual_type);
-             parm.frame_type = actual_type;
-           }
-         else if (TREE_CODE (actual_type) == REFERENCE_TYPE)
+         parm.by_ref = parm.pt_ref = false;
+         if (TREE_CODE (actual_type) == REFERENCE_TYPE)
            {
              /* If the user passes by reference, then we will save the
                 pointer to the original.  As noted in
@@ -3700,17 +3793,24 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
                 referenced item ends and then the coroutine is resumed,
                 we have UB; well, the user asked for it.  */
              actual_type = build_pointer_type (TREE_TYPE (actual_type));
-             parm.frame_type = actual_type;
              parm.pt_ref = true;
            }
          else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
+           parm.by_ref = true;
+
+         parm.frame_type = actual_type;
+
+         parm.this_ptr = is_this_parameter (arg);
+         /* See PR94807.  When a lambda is in a template instantiation, the
+            closure object is named 'this' instead of '__closure'.  */
+         if (lambda_p)
            {
-             parm.by_ref = true;
-             parm.frame_type = actual_type;
+             parm.lambda_cobj = DECL_NAME (arg) == closure_identifier;
+             gcc_checking_assert (!parm.this_ptr);
            }
          else
-           parm.frame_type = actual_type;
-         parm.this_ptr = is_this_parameter (arg);
+           parm.lambda_cobj = false;
+
          parm.trivial_dtor = TYPE_HAS_TRIVIAL_DESTRUCTOR (parm.frame_type);
          char *buf;
          if (DECL_NAME (arg))
@@ -3778,7 +3878,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   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);
@@ -3798,11 +3897,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   /* The decl_expr for the coro frame pointer, initialize to zero so that we
      can pass it to the IFN_CO_FRAME (since there's no way to pass a type,
      directly apparently).  This avoids a "used uninitialized" warning.  */
-  tree r = build_stmt (fn_start, DECL_EXPR, coro_fp);
   tree zeroinit = build1 (CONVERT_EXPR, coro_frame_ptr, integer_zero_node);
-  r = build2 (INIT_EXPR, TREE_TYPE (coro_fp), coro_fp, zeroinit);
-  r = coro_build_cvt_void_expr_stmt (r, fn_start);
-  add_stmt (r);
+  DECL_INITIAL (coro_fp) = zeroinit;
+  add_decl_expr (coro_fp);
 
   /* The CO_FRAME internal function is a mechanism to allow the middle end
      to adjust the allocation in response to optimisations.  We provide the
@@ -3813,7 +3910,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
     = build_call_expr_internal_loc (fn_start, IFN_CO_FRAME, size_type_node, 2,
                                    frame_size, coro_fp);
 
-  /* n4849 [dcl.fct.def.coroutine] / 10 (part1)
+  /* [dcl.fct.def.coroutine] / 10 (part1)
     The unqualified-id get_return_object_on_allocation_failure is looked up
     in the scope of the promise type by class member access lookup.  */
 
@@ -3839,7 +3936,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
     }
 
   /* Allocate the frame, this has several possibilities:
-     n4849 [dcl.fct.def.coroutine] / 9 (part 1)
+     [dcl.fct.def.coroutine] / 9 (part 1)
      The allocation function’s name is looked up in the scope of the promise
      type.  It's not a failure for it to be absent see part 4, below.  */
   tree nwname = ovl_op_identifier (false, NEW_EXPR);
@@ -3848,7 +3945,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   tree new_fn = NULL_TREE;
   if (fns && BASELINK_P (fns))
     {
-      /* n4849 [dcl.fct.def.coroutine] / 9 (part 2)
+      /* [dcl.fct.def.coroutine] / 9 (part 2)
        If the lookup finds an allocation function in the scope of the promise
        type, overload resolution is performed on a function call created by
        assembling an argument list.  The first argument is the amount of space
@@ -3856,9 +3953,28 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
        those of the original function.  */
       vec<tree, va_gc> *args = make_tree_vector ();
       vec_safe_push (args, resizeable); /* Space needed.  */
+
       for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
           arg = DECL_CHAIN (arg))
-       vec_safe_push (args, arg);
+       {
+         param_info *parm_i = param_uses->get (arg);
+         gcc_checking_assert (parm_i);
+         if (parm_i->lambda_cobj)
+           vec_safe_push (args, arg);
+         else if (parm_i->this_ptr)
+           {
+             /* We pass a reference to *this to the allocator lookup.  */
+             tree tt = TREE_TYPE (TREE_TYPE (arg));
+             tree this_ref = build1 (INDIRECT_REF, tt, arg);
+             tt = cp_build_reference_type (tt, false);
+             this_ref = convert_to_reference (tt, this_ref, CONV_STATIC,
+                                              LOOKUP_NORMAL , NULL_TREE,
+                                              tf_warning_or_error);
+             vec_safe_push (args, this_ref);
+           }
+         else
+           vec_safe_push (args, arg);
+       }
 
       /* We might need to check that the provided function is nothrow.  */
       tree func;
@@ -3869,7 +3985,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
       if (!new_fn || new_fn == error_mark_node)
        {
-         /* n4849 [dcl.fct.def.coroutine] / 9 (part 3)
+         /* [dcl.fct.def.coroutine] / 9 (part 3)
            If no viable function is found, overload resolution is performed
            again on a function call created by passing just the amount of
            space required as an argument of type std::size_t.  */
@@ -3895,7 +4011,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
     }
   else
     {
-      /* n4849 [dcl.fct.def.coroutine] / 9 (part 4)
+      /* [dcl.fct.def.coroutine] / 9 (part 4)
         If this lookup fails, the allocation function’s name is looked up in
         the global scope.  */
 
@@ -3906,7 +4022,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
       if (grooaf)
        {
-         /* n4849 [dcl.fct.def.coroutine] / 10 (part 2)
+         /* [dcl.fct.def.coroutine] / 10 (part 2)
           If any declarations (of the get return on allocation fail) are
           found, then the result of a call to an allocation function used
           to obtain storage for the coroutine state is assumed to return
@@ -3937,7 +4053,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
     }
 
   tree allocated = build1 (CONVERT_EXPR, coro_frame_ptr, new_fn);
-  r = build2 (INIT_EXPR, TREE_TYPE (coro_fp), coro_fp, allocated);
+  tree r = build2 (INIT_EXPR, TREE_TYPE (coro_fp), coro_fp, allocated);
   r = coro_build_cvt_void_expr_stmt (r, fn_start);
   add_stmt (r);
 
@@ -3947,38 +4063,32 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
 
   if (grooaf)
     {
-      /* n4849 [dcl.fct.def.coroutine] / 10 (part 3)
+      /* [dcl.fct.def.coroutine] / 10 (part 3)
         If the allocation function returns nullptr,the coroutine returns
         control to the caller of the coroutine and the return value is
         obtained by a call to T::get_return_object_on_allocation_failure(),
         where T is the promise type.  */
-       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);
+      gcc_checking_assert (same_type_p (fn_return_type, TREE_TYPE (grooaf)));
+      tree if_stmt = begin_if_stmt ();
+      tree cond = build1 (CONVERT_EXPR, coro_frame_ptr, integer_zero_node);
+      cond = build2 (EQ_EXPR, boolean_type_node, coro_fp, cond);
+      finish_if_stmt_cond (cond, if_stmt);
+      if (VOID_TYPE_P (fn_return_type))
+       {
+         /* Execute the get-return-object-on-alloc-fail call...  */
+         finish_expr_stmt (grooaf);
+         /* ... but discard the result, since we return void.  */
+         finish_return_stmt (NULL_TREE);
+       }
+      else
+       {
+         /* Get the fallback return object.  */
+         r = build_cplus_new (fn_return_type, grooaf, tf_warning_or_error);
+         finish_return_stmt (r);
+       }
+      finish_then_clause (if_stmt);
+      finish_if_stmt (if_stmt);
     }
 
   /* deref the frame pointer, to use in member access code.  */
@@ -4017,7 +4127,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   r = coro_build_cvt_void_expr_stmt (r, fn_start);
   add_stmt (r);
 
-  /* n4849 [dcl.fct.def.coroutine] /13
+  /* [dcl.fct.def.coroutine] /13
      When a coroutine is invoked, a copy is created for each coroutine
      parameter.  Each such copy is an object with automatic storage duration
      that is direct-initialized from an lvalue referring to the corresponding
@@ -4054,8 +4164,10 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
                                              false, tf_warning_or_error);
 
          /* Add this to the promise CTOR arguments list, accounting for
-            refs and this ptr.  */
-         if (parm.this_ptr)
+            refs and special handling for method this ptr.  */
+         if (parm.lambda_cobj)
+           vec_safe_push (promise_args, arg);
+         else if (parm.this_ptr)
            {
              /* We pass a reference to *this to the param preview.  */
              tree tt = TREE_TYPE (arg);
@@ -4070,17 +4182,16 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
            }
          else if (parm.by_ref)
            vec_safe_push (promise_args, fld_idx);
-         else if (parm.rv_ref)
-           vec_safe_push (promise_args, rvalue (fld_idx));
          else
            vec_safe_push (promise_args, arg);
 
          if (TYPE_NEEDS_CONSTRUCTING (parm.frame_type))
            {
              vec<tree, va_gc> *p_in;
-             if (parm.by_ref
-                 && classtype_has_non_deleted_move_ctor (parm.frame_type)
-                 && !classtype_has_non_deleted_copy_ctor (parm.frame_type))
+             if (CLASS_TYPE_P (parm.frame_type)
+                 && classtype_has_non_deleted_move_ctor (parm.frame_type))
+               p_in = make_tree_vector_single (move (arg));
+             else if (lvalue_p (arg))
                p_in = make_tree_vector_single (rvalue (arg));
              else
                p_in = make_tree_vector_single (arg);
@@ -4093,9 +4204,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
            }
          else
            {
-             if (parm.rv_ref)
-               r = convert_from_reference (arg);
-             else if (!same_type_p (parm.frame_type, DECL_ARG_TYPE (arg)))
+             if (!same_type_p (parm.frame_type, DECL_ARG_TYPE (arg)))
                r = build1_loc (DECL_SOURCE_LOCATION (arg), CONVERT_EXPR,
                                parm.frame_type, arg);
              else
@@ -4172,21 +4281,34 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
     {
       BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);
       DECL_SAVED_TREE (orig) = newbody;
+      /* Suppress warnings about the missing return value.  */
+      TREE_NO_WARNING (orig) = true;
       return false;
     }
 
   tree gro_context_body = push_stmt_list ();
-  tree gro = build_lang_decl (VAR_DECL, get_identifier ("coro.gro"),
-                             TREE_TYPE (get_ro));
-  DECL_CONTEXT (gro) = current_scope ();
-  add_decl_expr (gro);
-  tree gro_bind_vars = gro;
+  bool gro_is_void_p = VOID_TYPE_P (TREE_TYPE (get_ro));
 
+  tree gro = NULL_TREE;
+  tree gro_bind_vars = NULL_TREE;
   /* We have to sequence the call to get_return_object before initial
      suspend.  */
-  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);
+  if (gro_is_void_p)
+    finish_expr_stmt (get_ro);
+  else
+    {
+      gro = build_lang_decl (VAR_DECL, get_identifier ("coro.gro"),
+                             TREE_TYPE (get_ro));
+      DECL_CONTEXT (gro) = current_scope ();
+      DECL_ARTIFICIAL (gro) = true;
+      DECL_IGNORED_P (gro) = true;
+      add_decl_expr (gro);
+      gro_bind_vars = gro;
+
+      r = build2_loc (fn_start, INIT_EXPR, TREE_TYPE (gro), gro, get_ro);
+      r = coro_build_cvt_void_expr_stmt (r, fn_start);
+      add_stmt (r);
+    }
 
   /* Initialize the resume_idx_name to 0, meaning "not started".  */
   tree resume_idx_m
@@ -4222,23 +4344,34 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
   /* The ramp is done, we just need the return value.  */
   if (!same_type_p (TREE_TYPE (get_ro), fn_return_type))
     {
-      /* construct the return value with a single GRO param.  */
-      vec<tree, va_gc> *args = make_tree_vector_single (gro);
+      /* construct the return value with a single GRO param, if it's not
+        void.  */
+      vec<tree, va_gc> *args = NULL;
+      vec<tree, va_gc> **arglist = NULL;
+      if (!gro_is_void_p)
+       {
+         args = make_tree_vector_single (gro);
+         arglist = &args;
+       }
       r = build_special_member_call (NULL_TREE,
-                                    complete_ctor_identifier, &args,
+                                    complete_ctor_identifier, arglist,
                                     fn_return_type, LOOKUP_NORMAL,
                                     tf_warning_or_error);
       r = build_cplus_new (fn_return_type, r, tf_warning_or_error);
     }
-  else
+  else if (!gro_is_void_p)
     r = rvalue (gro); /* The GRO is the return value.  */
+  else
+    r = NULL_TREE;
 
   finish_return_stmt (r);
 
   /* Finish up the ramp function.  */
   BIND_EXPR_VARS (gro_context_bind) = gro_bind_vars;
   BIND_EXPR_BODY (gro_context_bind) = pop_stmt_list (gro_context_body);
+  TREE_SIDE_EFFECTS (gro_context_bind) = true;
   BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);
+  TREE_SIDE_EFFECTS (ramp_bind) = true;
 
   /* 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
@@ -4357,6 +4490,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
          BLOCK_SUPERCONTEXT (replace_blk) = tcb_block;
          BLOCK_SUBBLOCKS (tcb_block) = replace_blk;
          BIND_EXPR_BLOCK (fnbody) = tcb_block;
+         TREE_SIDE_EFFECTS (fnbody) = true;
        }
     }
   else if (pedantic)