Re: [C++ coroutines 3/6] Front end parsing and transforms.

2019-11-19 Thread Nathan Sidwell

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 ?



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

Re: [C++ coroutines 3/6] Front end parsing and transforms.

2019-11-18 Thread Nathan Sidwell

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 =