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