[PATCH] coroutines: Fix handling of conditional statements [PR94288]

Iain Sandoe iain@sandoe.co.uk
Mon Apr 20 16:48:51 GMT 2020


Hi,

Normally, when we find a statement containing an await expression
this will be expanded to a statement list implementing the control
flow implied.  The expansion process successively replaces each
await expression in a statement with the result of its await_resume().

In the case of conditional statements (if, while, do, switch) the
expansion of the condition (or expression in the case of do-while)
cannot take place 'inline', leading to the PR.

The solution is to evaluate the expression separately, and to
transform while and do-while loops into endless loops with a break
on the required condition.

In fixing this, I realised that I'd also made a thinko in the case
of expanding truth-and/or-if expressions, where one arm of the
expression might need to be short-circuited.  The mechanism for
expanding via the tree walk will not work correctly in this case and
we need to pre-expand any truth-and/or-if with an await expression
on its conditionally-taken arm.  This applies to any statement with
truth-and/or-if expressions, so can be handled generically.

This has been tested in various permutations (including without-
checking) on x86_64-darwin, now testing on x86/power Linux.

The testcases do not include the testcase from the PR since that
fails because of PR94661 (it can be included when that’s resolved).

The testcases are appended as a text file.

OK for master, assuming that regstraps on x86/power Linux are OK?
thanks
Iain


gcc/cp/ChangeLog:

2020-04-20  Iain Sandoe  <iain@sandoe.co.uk>

	PR c++/94288
	* coroutines.cc (await_statement_expander): Simplify cases.
	(struct susp_frame_data): Add fields for truth and/or if
	cases, rename one field.
	(analyze_expression_awaits): New.
	(expand_one_truth_if): New.
	(add_var_to_bind): New helper.
	(coro_build_add_if_not_cond_break): New helper.
	(await_statement_walker): Handle conditional expressions,
	handle expansion of truth-and/or-if cases.
	(bind_expr_find_in_subtree): New, checking-only.
	(coro_body_contains_bind_expr_p): New, checking-only.
	(morph_fn_to_coro): Ensure that we have a top level bind
	expression.

gcc/testsuite/ChangeLog:

2020-04-20  Iain Sandoe  <iain@sandoe.co.uk>

	PR c++/94288
	* g++.dg/coroutines/torture/co-await-18-if-cond.C: New test.
	* g++.dg/coroutines/torture/co-await-19-while-cond.C: New test.
	* g++.dg/coroutines/torture/co-await-20-do-while-cond.C: New test.
	* g++.dg/coroutines/torture/co-await-21-switch-value.C: New test.
	* g++.dg/coroutines/torture/co-await-22-truth-and-of-if.C: New test.
	* g++.dg/coroutines/torture/co-ret-16-simple-control-flow.C: New test.
---
 gcc/cp/coroutines.cc | 433 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 423 insertions(+), 10 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 0a8e7521c4f..4e0d50a529f 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -1627,9 +1627,8 @@ await_statement_expander (tree *stmt, int *do_subtree, void *d)
   tree res = NULL_TREE;
 
   /* Process a statement at a time.  */
-  if (TREE_CODE (*stmt) == BIND_EXPR)
-    res = cp_walk_tree (&BIND_EXPR_BODY (*stmt), await_statement_expander,
-			d, NULL);
+  if (STATEMENT_CLASS_P (*stmt) || TREE_CODE (*stmt) == BIND_EXPR)
+    return NULL_TREE; /* Just process the sub-trees.  */
   else if (TREE_CODE (*stmt) == STATEMENT_LIST)
     {
       tree_stmt_iterator i;
@@ -1642,8 +1641,6 @@ await_statement_expander (tree *stmt, int *do_subtree, void *d)
 	}
       *do_subtree = 0; /* Done subtrees.  */
     }
-  else if (STATEMENT_CLASS_P (*stmt))
-    return NULL_TREE; /* Process the sub-trees.  */
   else if (EXPR_P (*stmt))
     {
       process_one_statement (stmt, d);
@@ -2586,12 +2583,14 @@ struct susp_frame_data
   vec<tree, va_gc> *block_stack; /* Track block scopes.  */
   vec<tree, va_gc> *bind_stack;  /* Track current bind expr.  */
   unsigned await_number;	 /* Which await in the function.  */
-  unsigned condition_number;	 /* Which replaced condition in the fn.  */
+  unsigned cond_number;		 /* Which replaced condition in the fn.  */
   /* Temporary values for one statement or expression being analyzed.  */
   hash_set<tree> captured_temps; /* The suspend captured these temps.  */
   vec<tree, va_gc> *to_replace;  /* The VAR decls to replace.  */
+  hash_set<tree> *truth_aoif_to_expand; /* The set of TRUTH exprs to expand.  */
   unsigned saw_awaits;		 /* Count of awaits in this statement  */
   bool captures_temporary;	 /* This expr captures temps by ref.  */
+  bool needs_truth_if_exp;	 /* We must expand a truth_if expression.  */
 };
 
 /* Walk the sub-tree looking for call expressions that both capture
@@ -2895,6 +2894,178 @@ maybe_promote_captured_temps (tree *stmt, void *d)
   return NULL_TREE;
 }
 
+/* Lightweight callback to determine two key factors:
+   1) If the statement/expression contains any await expressions.
+   2) If the statement/expression potentially requires a re-write to handle
+      TRUTH_{AND,OR}IF_EXPRs since, in most cases, they will need expansion
+      so that the await expressions are not processed in the case of the
+      short-circuit arm.
+   CO_YIELD expressions are re-written to their underlying co_await.  */
+
+static tree
+analyze_expression_awaits (tree *stmt, int *do_subtree, void *d)
+{
+  susp_frame_data *awpts = (susp_frame_data *) d;
+
+  switch (TREE_CODE (*stmt))
+    {
+      default: return NULL_TREE;
+      case CO_YIELD_EXPR:
+	/* co_yield is syntactic sugar, re-write it to co_await.  */
+	*stmt = TREE_OPERAND (*stmt, 1);
+	/* FALLTHROUGH */
+      case CO_AWAIT_EXPR:
+	awpts->saw_awaits++;
+	break;
+      case TRUTH_ANDIF_EXPR:
+      case TRUTH_ORIF_EXPR:
+	{
+	  /* We don't need special action for awaits in the always-executed
+	     arm of a TRUTH_IF.  */
+	  if (tree res = cp_walk_tree (&TREE_OPERAND (*stmt, 0),
+				       analyze_expression_awaits, d, NULL))
+	    return res;
+	  /* However, if there are await expressions on the conditionally
+	     executed branch, we must expand the TRUTH_IF to ensure that the
+	     expanded await expression control-flow is fully contained in the
+	     conditionally executed code.  */
+	  unsigned aw_count = awpts->saw_awaits;
+	  if (tree res = cp_walk_tree (&TREE_OPERAND (*stmt, 1),
+				       analyze_expression_awaits, d, NULL))
+	    return res;
+	  if (awpts->saw_awaits > aw_count)
+	    {
+	      awpts->truth_aoif_to_expand->add (*stmt);
+	      awpts->needs_truth_if_exp = true;
+	    }
+	  /* We've done the sub-trees here.  */
+	  *do_subtree = 0;
+	}
+	break;
+    }
+
+  return NULL_TREE; /* Recurse until done.  */
+}
+
+/* Given *EXPR
+   If EXPR contains a TRUTH_{AND,OR}IF_EXPR, TAOIE with an await expr on
+   the conditional branch expand this to:
+
+   bool not_expr = TAOIE == TRUTH_ORIF_EXPR ? NOT : NOP;
+   A) bool t = always exec expr
+      if (not_expr (t))
+   B)   t = conditionally exec expr
+   c) EXPR' = EXPR with  TAOIE replaced by t.
+
+   Then repeat this for A, B and C.  */
+
+struct truth_if_transform {
+  tree *orig_stmt;
+  tree scratch_var;
+  hash_set<tree> *truth_aoif_to_expand;
+};
+
+static tree
+expand_one_truth_if (tree *expr, int *do_subtree, void *d)
+{
+  truth_if_transform *xform = (truth_if_transform *) d;
+
+  bool needs_not = false;
+  switch (TREE_CODE (*expr))
+    {
+      default: break;
+      case TRUTH_ORIF_EXPR:
+	needs_not = true;
+	/* FALLTHROUGH */
+      case TRUTH_ANDIF_EXPR:
+	{
+	  if (!xform->truth_aoif_to_expand->contains (*expr))
+	    break;
+
+	  location_t sloc = EXPR_LOCATION (*expr);
+	  tree type = TREE_TYPE (xform->scratch_var);
+	  gcc_checking_assert (TREE_CODE (type) == BOOLEAN_TYPE);
+	  tree new_list = push_stmt_list ();
+	  /* Init our scratch with the unconditionally-evaluated expr.  */
+	  tree new_s = build2_loc (sloc, INIT_EXPR, boolean_type_node,
+				   xform->scratch_var,
+				   TREE_OPERAND (*expr, 0));
+	  finish_expr_stmt (new_s);
+	  tree *pre = tsi_stmt_ptr (tsi_last (new_list));
+	  tree if_cond = xform->scratch_var;
+	  if (needs_not)
+	    if_cond = build1 (TRUTH_NOT_EXPR, boolean_type_node, if_cond);
+	  tree if_stmt = begin_if_stmt ();
+	  finish_if_stmt_cond (if_cond, if_stmt);
+	  /* If we take the if branch, then overwrite scratch with the cond
+	     executed branch.  */
+	  new_s = build2 (INIT_EXPR, boolean_type_node,
+			  xform->scratch_var, TREE_OPERAND (*expr, 1));
+	  finish_expr_stmt (new_s);
+	  finish_then_clause (if_stmt);
+	  finish_if_stmt (if_stmt);
+	  *expr = xform->scratch_var; /* now contains the result.  */
+	  /* So now we've got a statement list expanding one TAOIe.  */
+	  add_stmt (*xform->orig_stmt);
+	  tree *post = tsi_stmt_ptr (tsi_last (new_list));
+	  *xform->orig_stmt = pop_stmt_list (new_list);
+	  /* Now recurse into the pre, if and post parts.  */
+	  truth_if_transform sub_data = {pre, xform->scratch_var,
+					 xform->truth_aoif_to_expand};
+	  if (tree res = cp_walk_tree (pre, expand_one_truth_if, &sub_data,
+				       NULL))
+	    return res;
+	  sub_data.orig_stmt = &THEN_CLAUSE (if_stmt);
+	  if (tree res = cp_walk_tree (&THEN_CLAUSE (if_stmt),
+				       expand_one_truth_if, &sub_data, NULL))
+	    return res;
+	  sub_data.orig_stmt = post;
+	  if (tree res = cp_walk_tree (post, expand_one_truth_if, &sub_data,
+				       NULL))
+	    return res;
+	  /* We've done the sub-trees here.  */
+	  *do_subtree = 0;
+	}
+	break;
+    }
+  return NULL_TREE;
+}
+
+/* Helper that adds a new variable of VAR_TYPE to a bind scope BIND, the
+   name is made up from NAM_ROOT, NAM_VERS.  */
+
+static tree
+add_var_to_bind (tree& bind, tree var_type,
+		 const char *nam_root, unsigned nam_vers)
+{
+
+  tree b_vars = BIND_EXPR_VARS (bind);
+  /* Build a variable to hold the condition, this will be included in the
+     frame as a local var.  */
+  char *nam = xasprintf ("%s.%d", nam_root, nam_vers);
+  tree newvar = build_lang_decl (VAR_DECL, get_identifier (nam), var_type);
+  free (nam);
+  DECL_CHAIN (newvar) = b_vars;
+  BIND_EXPR_VARS (bind) = newvar;
+  return newvar;
+}
+
+/* Helper to build and add if (!cond) break;  */
+
+static void
+coro_build_add_if_not_cond_break (tree cond)
+{
+  tree if_stmt = begin_if_stmt ();
+  tree invert = build1 (TRUTH_NOT_EXPR, boolean_type_node, cond);
+  finish_if_stmt_cond (invert, if_stmt);
+  finish_break_stmt ();
+  finish_then_clause (if_stmt);
+  finish_if_stmt (if_stmt);
+}
+
+/* Tree walk callback to analyze, register and pre-process statements that
+   contain await expressions.  */
+
 static tree
 await_statement_walker (tree *stmt, int *do_subtree, void *d)
 {
@@ -2904,6 +3075,9 @@ await_statement_walker (tree *stmt, int *do_subtree, void *d)
   /* Process a statement at a time.  */
   if (TREE_CODE (*stmt) == BIND_EXPR)
     {
+      /* For conditional expressions, we might wish to add an artificial var
+	 to their containing bind expr.  */
+      vec_safe_push (awpts->bind_stack, *stmt);
       /* 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.  */
       tree blk = BIND_EXPR_BLOCK (*stmt);
@@ -2911,7 +3085,9 @@ await_statement_walker (tree *stmt, int *do_subtree, void *d)
       res = cp_walk_tree (&BIND_EXPR_BODY (*stmt), await_statement_walker,
 			  d, NULL);
       awpts->block_stack->pop ();
+      awpts->bind_stack->pop ();
       *do_subtree = 0; /* Done subtrees.  */
+      return res;
     }
   else if (TREE_CODE (*stmt) == STATEMENT_LIST)
     {
@@ -2924,13 +3100,205 @@ await_statement_walker (tree *stmt, int *do_subtree, void *d)
 	    return res;
 	}
       *do_subtree = 0; /* Done subtrees.  */
+      return NULL_TREE;
     }
-  else if (STATEMENT_CLASS_P (*stmt))
-    return NULL_TREE; /* Process the subtrees.  */
+
+  /* We have something to be handled as a single statement.  */
+  hash_set<tree> visited;
+  awpts->saw_awaits = 0;
+  hash_set<tree> truth_aoif_to_expand;
+  awpts->truth_aoif_to_expand = &truth_aoif_to_expand;
+  awpts->needs_truth_if_exp = false;
+
+  if (STATEMENT_CLASS_P (*stmt))
+    switch (TREE_CODE (*stmt))
+      {
+	/* Unless it's a special case, just walk the subtrees as usual.  */
+	default: return NULL_TREE;
+
+	/* When we have a conditional expression, which contains one or more
+	   await expressions, we have to break the condition out into a
+	   regular statement so that the control flow introduced by the await
+	   transforms can be implemented.  */
+	case IF_STMT:
+	  {
+	    /* Transform 'if (cond with awaits) then stmt1 else stmt2' into
+	       bool cond = cond with awaits.
+	       if (cond) then stmt1 else stmt2.  */
+	    tree if_stmt = *stmt;
+	    /* We treat the condition as if it was a stand-alone statement,
+	       to see if there are any await expressions which will be analysed
+	       and registered.  */
+	    if ((res = cp_walk_tree (&IF_COND (if_stmt),
+		analyze_expression_awaits, d, &visited)))
+	      return res;
+	    if (!awpts->saw_awaits)
+	      return NULL_TREE; /* Nothing special to do here.  */
+
+	    gcc_checking_assert (!awpts->bind_stack->is_empty());
+	    tree& bind_expr = awpts->bind_stack->last ();
+	    tree newvar = add_var_to_bind (bind_expr, boolean_type_node,
+					   "ifcd", awpts->cond_number++);
+	    tree insert_list = push_stmt_list ();
+	    tree cond_inner = IF_COND (if_stmt);
+	    if (TREE_CODE (cond_inner) == CLEANUP_POINT_EXPR)
+	      cond_inner = TREE_OPERAND (cond_inner, 0);
+	    add_decl_expr (newvar);
+	    location_t sloc = EXPR_LOCATION (IF_COND (if_stmt));
+	    /* We want to initialize the new variable with the expression
+	       that contains the await(s) and potentially also needs to
+	       have truth_if expressions expanded.  */
+	    tree new_s = build2_loc (sloc, MODIFY_EXPR, boolean_type_node,
+				     newvar, cond_inner);
+	    finish_expr_stmt (new_s);
+	    if (awpts->needs_truth_if_exp)
+	      {
+		tree *sp = tsi_stmt_ptr (tsi_last (insert_list));
+		truth_if_transform xf = {sp, newvar, &truth_aoif_to_expand};
+		if ((res = cp_walk_tree (sp, expand_one_truth_if, &xf, NULL)))
+		  return res;
+	      }
+	    IF_COND (if_stmt) = newvar;
+	    add_stmt (if_stmt);
+	    *stmt = pop_stmt_list (insert_list);
+	    /* So now walk the new statement list.  */
+	    res = cp_walk_tree (stmt, await_statement_walker, d, NULL);
+	    *do_subtree = 0; /* Done subtrees.  */
+	    return res;
+	  }
+	  break;
+	case WHILE_STMT:
+	  {
+	    /* We turn 'while (cond with awaits) stmt' into
+	       while (true) {
+		  if (!(cond with awaits))
+		    break;
+		  stmt..
+		} */
+	    tree while_stmt = *stmt;
+	    if ((res = cp_walk_tree (&WHILE_COND (while_stmt),
+		analyze_expression_awaits, d, &visited)))
+	      return res;
+	    if (!awpts->saw_awaits)
+	      return NULL_TREE; /* Nothing special to do here.  */
+
+	    tree insert_list = push_stmt_list ();
+	    coro_build_add_if_not_cond_break (WHILE_COND (while_stmt));
+	    /* The original while body.  */
+	    add_stmt (WHILE_BODY (while_stmt));
+	    /* The new while body.  */
+	    WHILE_BODY (while_stmt) = pop_stmt_list (insert_list);
+	    WHILE_COND (while_stmt) = boolean_true_node;
+	    /* So now walk the new statement list.  */
+	    res = cp_walk_tree (&WHILE_BODY (while_stmt),
+				await_statement_walker, d, NULL);
+	    *do_subtree = 0; /* Done subtrees.  */
+	    return res;
+	  }
+	  break;
+	case DO_STMT:
+	  {
+	    /* We turn do stmt while (cond with awaits) into:
+	       do {
+		  stmt..
+		  if (!(cond with awaits))
+		    break;
+	       } while (true); */
+	    tree do_stmt = *stmt;
+	    if ((res = cp_walk_tree (&DO_COND (do_stmt),
+		analyze_expression_awaits, d, &visited)))
+	      return res;
+	    if (!awpts->saw_awaits)
+	      return NULL_TREE; /* Nothing special to do here.  */
+
+	    tree insert_list = push_stmt_list ();
+	    /* The original do stmt body.  */
+	    add_stmt (DO_BODY (do_stmt));
+	    coro_build_add_if_not_cond_break (DO_COND (do_stmt));
+	    /* The new while body.  */
+	    DO_BODY (do_stmt) = pop_stmt_list (insert_list);
+	    DO_COND (do_stmt) = boolean_true_node;
+	    /* So now walk the new statement list.  */
+	    res = cp_walk_tree (&DO_BODY (do_stmt), await_statement_walker,
+				d, NULL);
+	    *do_subtree = 0; /* Done subtrees.  */
+	    return res;
+
+	  }
+	  break;
+	case SWITCH_STMT:
+	  {
+	    /* We turn 'switch (cond with awaits) stmt' into
+	       switch_type cond = cond with awaits
+	       switch (cond) stmt.  */
+	    tree sw_stmt = *stmt;
+	    if ((res = cp_walk_tree (&SWITCH_STMT_COND (sw_stmt),
+		analyze_expression_awaits, d, &visited)))
+	      return res;
+	    if (!awpts->saw_awaits)
+	      return NULL_TREE; /* Nothing special to do here.  */
+
+	    gcc_checking_assert (!awpts->bind_stack->is_empty());
+	    /* Build a variable to hold the condition, this will be
+		   included in the frame as a local var.  */
+	    tree& bind_expr = awpts->bind_stack->last ();
+	    tree sw_type = SWITCH_STMT_TYPE (sw_stmt);
+	    tree newvar = add_var_to_bind (bind_expr, sw_type, "swch",
+					   awpts->cond_number++);
+	    tree insert_list = push_stmt_list ();
+	    add_decl_expr (newvar);
+
+	    tree cond_inner = SWITCH_STMT_COND (sw_stmt);
+	    if (TREE_CODE (cond_inner) == CLEANUP_POINT_EXPR)
+	      cond_inner = TREE_OPERAND (cond_inner, 0);
+	    location_t sloc = EXPR_LOCATION (SWITCH_STMT_COND (sw_stmt));
+	    tree new_s = build2_loc (sloc, INIT_EXPR, sw_type, newvar,
+				     cond_inner);
+	    finish_expr_stmt (new_s);
+	    SWITCH_STMT_COND (sw_stmt) = newvar;
+	    /* Now add the switch statement with the condition re-
+		   written to use the local var.  */
+	    add_stmt (sw_stmt);
+	    *stmt = pop_stmt_list (insert_list);
+	    /* Process the expanded list.  */
+	    res = cp_walk_tree (stmt, await_statement_walker,
+				d, NULL);
+	    *do_subtree = 0; /* Done subtrees.  */
+	    return res;
+	  }
+	  break;
+      }
   else if (EXPR_P (*stmt))
     {
-      res = maybe_promote_captured_temps (stmt, d);
+      if ((res = cp_walk_tree (stmt, analyze_expression_awaits, d, &visited)))
+	return res;
       *do_subtree = 0; /* Done subtrees.  */
+      if (!awpts->saw_awaits)
+	return NULL_TREE; /* Nothing special to do here.  */
+
+      /* Unless we need to expand any truth-and/or-if expressions, then the
+	 remaining action is to check for temporaries to await expressions
+	 captured by refence.  */
+      if (!awpts->needs_truth_if_exp)
+	return maybe_promote_captured_temps (stmt, d);
+
+      gcc_checking_assert (!awpts->bind_stack->is_empty());
+      tree& bind_expr = awpts->bind_stack->last ();
+      /* Build a variable to hold the condition, this will be
+      included in the frame as a local var.  */
+      tree newvar = add_var_to_bind (bind_expr, boolean_type_node,
+				     "taoi", awpts->cond_number++);
+      tree insert_list = push_stmt_list ();
+      add_decl_expr (newvar);
+      add_stmt (*stmt);
+      tree *sp = tsi_stmt_ptr (tsi_last (insert_list));
+      *stmt = pop_stmt_list (insert_list);
+
+      truth_if_transform xf = {sp, newvar, &truth_aoif_to_expand};
+      if ((res = cp_walk_tree (sp, expand_one_truth_if, &xf, NULL)))
+	return res;
+      /* Process the expanded trees.  */
+      return cp_walk_tree (stmt, await_statement_walker, d, NULL);
     }
  
   /* Continue recursion, if needed.  */
@@ -3072,6 +3440,27 @@ act_des_fn (tree orig, tree fn_type, tree coro_frame_ptr, const char* name)
   return fn;
 }
 
+#if CHECKING_P
+/* Return a bind expression if we see one, else NULL_TREE.  */
+static tree
+bind_expr_find_in_subtree (tree *stmt, int *, void *)
+{
+  if (TREE_CODE (*stmt) == BIND_EXPR)
+    return *stmt;
+  return NULL_TREE;
+}
+
+/* Return the first bind expression that the sub-tree given by STMT
+   contains.  */
+
+static tree
+coro_body_contains_bind_expr_p (tree *stmt)
+{
+  hash_set<tree> visited;
+  return cp_walk_tree (stmt, bind_expr_find_in_subtree, NULL, &visited);
+}
+#endif
+
 /* Here we:
    a) Check that the function and promise type are valid for a
       coroutine.
@@ -3159,6 +3548,30 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
       TREE_OPERAND (body_start, 0) = push_stmt_list ();
     }
 
+  /* We can be presented with a function that currently has no outer bind
+     expression.  We will insert bind scopes in expanding await expressions,
+     and therefore need a top level to the tree, so synthesize an outer bind
+     expression and scope.  */
+  tree check_bind = expr_first (fnbody);
+  if (check_bind && TREE_CODE (check_bind) != BIND_EXPR)
+    {
+      tree update_body = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
+      tree blk = make_node (BLOCK);
+      gcc_checking_assert (!coro_body_contains_bind_expr_p (&fnbody));
+      BIND_EXPR_BLOCK (update_body) = blk;
+      if (TREE_CODE (fnbody) == STATEMENT_LIST)
+	BIND_EXPR_BODY (update_body) = fnbody;
+      else
+	{
+	  tree tlist = NULL_TREE;
+	  append_to_statement_list_force (fnbody, &tlist);
+	  BIND_EXPR_BODY (update_body) = tlist;
+	}
+      tree new_body_list = NULL_TREE;
+      append_to_statement_list_force (update_body, &new_body_list);
+      fnbody = new_body_list;
+    }
+
   /* Create the coro frame type, as far as it can be known at this stage.
      1. Types we already know.  */
 
@@ -3308,7 +3721,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
      vars) they will get added to the coro frame along with other locals.  */
   susp_frame_data body_aw_points
     = {&field_list, handle_type, NULL, NULL, 0, 0,
-       hash_set<tree> (), NULL, 0, false};
+       hash_set<tree> (), NULL, NULL, 0, false, false};
   body_aw_points.block_stack = make_tree_vector ();
   body_aw_points.bind_stack = make_tree_vector ();
   body_aw_points.to_replace = make_tree_vector ();
-- 
2.24.1

-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: 0002-pr94228-tests.patch.txt
URL: <https://gcc.gnu.org/pipermail/gcc-patches/attachments/20200420/6c60aef7/attachment-0001.txt>
-------------- next part --------------




More information about the Gcc-patches mailing list