Use promise in coroutine frame in actor function.
authorBin Cheng <bin.cheng@linux.alibaba.com>
Thu, 30 Jan 2020 04:33:47 +0000 (12:33 +0800)
committerBin Cheng <bin.cheng@linux.alibaba.com>
Thu, 30 Jan 2020 04:35:53 +0000 (12:35 +0800)
By standard, coroutine body should be encapsulated in try-catch block
as following:
  try {
    // coroutine body
  } catch(...) {
    promise.unhandled_exception();
  }
Given above try-catch block is implemented in the coroutine actor
function called by coroutine ramp function, so the promise should
be accessed via actor function's coroutine frame pointer argument,
rather than the ramp function's coroutine frame variable.

This patch cleans code a bit to make fix easy.

gcc/cp
    * coroutines.cc (act_des_fn): New.
    (morph_fn_to_coro): Call act_des_fn to build actor/destroy decls.
    Access promise via actor function's frame pointer argument.
    (build_actor_fn, build_destroy_fn): Use frame pointer argument.

gcc/cp/ChangeLog
gcc/cp/coroutines.cc

index 335652451bd8c2fa5bb0bfe057f88d42266f1e08..a402b975ce3bab09e9cfed0781f2fd70fc94333b 100644 (file)
@@ -1,3 +1,10 @@
+2020-01-30  Bin Cheng  <bin.cheng@linux.alibaba.com>
+
+       * coroutines.cc (act_des_fn): New.
+       (morph_fn_to_coro): Call act_des_fn to build actor/destroy decls.
+       Access promise via actor function's frame pointer argument.
+       (build_actor_fn, build_destroy_fn): Use frame pointer argument.
+
 2020-01-30  Bin Cheng  <bin.cheng@linux.alibaba.com>
 
        * coroutines.cc (co_await_expander): Handle type conversion case.
index 7deb6f6e3e455f65643835cbdb5092db74b68d52..f7f85cb7643b5b604716e1da800830229a5ff25b 100644 (file)
@@ -1828,11 +1828,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   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;
+  tree actor_fp = DECL_ARGUMENTS (actor);
 
   /* A void return.  */
   tree resdecl = build_decl (loc, RESULT_DECL, 0, void_type_node);
@@ -2219,12 +2215,7 @@ 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;
+  tree destr_fp = DECL_ARGUMENTS (destroy);
 
   /* A void return.  */
   tree resdecl = build_decl (loc, RESULT_DECL, 0, void_type_node);
@@ -2865,6 +2856,24 @@ register_local_var_uses (tree *stmt, int *do_subtree, void *d)
   return NULL_TREE;
 }
 
+/* Build, return FUNCTION_DECL node with its coroutine frame pointer argument
+   for either actor or destroy functions.  */
+
+static tree
+act_des_fn (tree orig, tree fn_type, tree coro_frame_ptr, const char* name)
+{
+  tree fn_name = get_fn_local_identifier (orig, name);
+  tree fn = build_lang_decl (FUNCTION_DECL, fn_name, fn_type);
+  DECL_CONTEXT (fn) = DECL_CONTEXT (orig);
+  DECL_INITIAL (fn) = error_mark_node;
+  tree id = get_identifier ("frame_ptr");
+  tree fp = build_lang_decl (PARM_DECL, id, coro_frame_ptr);
+  DECL_CONTEXT (fp) = fn;
+  DECL_ARG_TYPE (fp) = type_passed_as (coro_frame_ptr);
+  DECL_ARGUMENTS (fn) = fp;
+  return fn;
+}
+
 /* Here we:
    a) Check that the function and promise type are valid for a
       coroutine.
@@ -2991,17 +3000,9 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
     = 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;
+  /* Declare the actor and destroyer function.  */
+  tree actor = act_des_fn (orig, act_des_fn_type, coro_frame_ptr, "actor");
+  tree destroy = act_des_fn (orig, act_des_fn_type, coro_frame_ptr, "destroy");
 
   /* Build our dummy coro frame layout.  */
   coro_frame_type = begin_class_definition (coro_frame_type);
@@ -3598,39 +3599,41 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
       tree ueh_meth
        = lookup_promise_method (orig, coro_unhandled_exception_identifier,
                                 fn_start, /*musthave=*/true);
+      /* actor's version of the promise.  */
+      tree actor_frame = build1_loc (fn_start, INDIRECT_REF, coro_frame_type,
+                                    DECL_ARGUMENTS (actor));
+      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);
       /* Build promise.unhandled_exception();  */
       tree ueh
-       = build_new_method_call (p, ueh_meth, NULL, NULL_TREE, LOOKUP_NORMAL,
+       = build_new_method_call (ap, 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 ();
+      fnbody = build_stmt (fn_start, TRY_BLOCK, fnbody, NULL_TREE);
+      TRY_HANDLERS (fnbody) = 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));
+      TRY_HANDLERS (fnbody) = pop_stmt_list (TRY_HANDLERS (fnbody));
       /* 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);
+         fnbody = build3 (BIND_EXPR, void_type_node, NULL, fnbody, 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;
+         BIND_EXPR_BLOCK (fnbody) = tcb_block;
        }
-      else
-       fnbody = tcb;
     }
   else if (pedantic)
     {