Re: [C++ coroutines 3/6] Front end parsing and transforms.
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.
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 =