public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
From: Nathan Sidwell <nathan@acm.org>
To: Iain Sandoe <iain@sandoe.co.uk>,
	GCC Patches <gcc-patches@gcc.gnu.org>,
	libstdc++ <libstdc++@gcc.gnu.org>
Subject: Re: [C++ coroutines 3/6] Front end parsing and transforms.
Date: Mon, 18 Nov 2019 13:23:00 -0000	[thread overview]
Message-ID: <fa7377e6-202c-29a8-d2d8-c094de7b2e86@acm.org> (raw)
In-Reply-To: <EC0A5CB6-61E5-4F68-AB6C-CF8BACC7FFD7@sandoe.co.uk>

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

  parent reply	other threads:[~2019-11-18 13:23 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-11-17 10:23 [C++ coroutines 0/6] Implement C++ coroutines Iain Sandoe
2019-11-17 10:24 ` [C++ coroutines 1/6] Common code and base definitions Iain Sandoe
2019-11-17 10:24   ` [C++ coroutines 2/6] Define builtins and internal functions Iain Sandoe
2019-11-17 10:26     ` [C++ coroutines 3/6] Front end parsing and transforms Iain Sandoe
2019-11-17 10:26       ` [C++ coroutines 4/6] Middle end expanders " Iain Sandoe
2019-11-17 10:27         ` [C++ coroutines 5/6] Standard library header Iain Sandoe
2019-11-17 10:28           ` [C++ coroutines 6/6] Testsuite Iain Sandoe
2019-11-19 10:01             ` Richard Biener
2020-01-09 12:40               ` [C++ coroutines 6/7, v2] Testsuite Iain Sandoe
2020-01-09 13:00                 ` Iain Sandoe
2020-01-09 13:17                 ` Richard Biener
2019-11-20 11:13             ` [C++ coroutines 6/6] Testsuite JunMa
2019-11-20 11:22               ` Iain Sandoe
2019-11-20 13:11                 ` JunMa
2019-11-17 16:19           ` [C++ coroutines 5/6] Standard library header Jonathan Wakely
2020-01-09 12:39             ` [C++ coroutines 5/7, v2] " Iain Sandoe
2020-01-09 13:50               ` Jonathan Wakely
2020-01-09 19:57                 ` [C++ coroutines 5/7, v3] " Iain Sandoe
2020-01-09 21:21                   ` Jonathan Wakely
2019-11-19 10:00         ` [C++ coroutines 4/6] Middle end expanders and transforms Richard Biener
2020-01-09 12:38           ` [C++ coroutines 4/7, v2] " Iain Sandoe
2020-01-09 14:38             ` Richard Biener
2019-11-18 13:23       ` Nathan Sidwell [this message]
2019-11-19 18:40       ` [C++ coroutines 3/6] Front end parsing " Nathan Sidwell
2020-01-09 12:37         ` [C++ coroutines 3/7, v2] " Iain Sandoe
2019-11-17 16:54     ` [C++ coroutines 2/6] Define builtins and internal functions Jeff Law
2020-01-09 12:36       ` [C++ coroutines 2/7, v2] " Iain Sandoe
2019-11-17 15:49   ` [C++ coroutines 1/6] Common code and base definitions Jeff Law
2020-01-09 12:36     ` [C++ coroutines 1/7] " Iain Sandoe
2019-11-18 12:35 ` [C++ coroutines 0/6] Implement C++ coroutines Nathan Sidwell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=fa7377e6-202c-29a8-d2d8-c094de7b2e86@acm.org \
    --to=nathan@acm.org \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=iain@sandoe.co.uk \
    --cc=libstdc++@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).