Jason Merrill <ja...@redhat.com> writes:

> On 7/31/24 6:54 AM, Arsen Arsenović wrote:
>> Tested on x86_64-pc-linux-gnu.  OK for trunk?
>> TIA, have a lovely day.
>> ---------- >8 ----------
>> By doing so, we can get diagnostics in template decls when we know we
>> can.  For instance, in the following:
>>    awaitable g();
>>    template<typename>
>>    task f()
>>    {
>>      co_await g();
>>      co_yield 1;
>>      co_return "foo";
>>    }
>> ... the coroutine promise type in each statement is always
>> std::coroutine_handle<task>::promise_type, and all of the operands are
>> not type-dependent, so we can always compute the resulting types (and
>> expected types) of these expressions and statements.
>> Also, when we do not know the type of the CO_AWAIT_EXPR or
>> CO_YIELD_EXPR, we now return NULL_TREE as the type rather than
>> unknown_type_node.  This is more correct, since the type is not unknown,
>> it just isn't determined yet.  This also means we can remove the
>> CO_AWAIT_EXPR and CO_YIELD_EXPR special-cases from
>> type_dependent_expression_p.
>> PR c++/112341 - error: insufficient contextual information to determine type
>> on co_await result in function template
>> gcc/cp/ChangeLog:
>>      PR c++/112341
>>      * coroutines.cc (struct coroutine_info): Also cache the
>>      traits type.
>>      (ensure_coro_initialized): New function.  Makes sure we have
>>      initialized the coroutine state successfully, or informs the
>>      caller should it fail to do so.  Extracted from
>>      coro_promise_type_found_p.
>>      (coro_get_traits_class): New function.  Gets the (cached)
>>      coroutine traits type for a given coroutine.  Extracted from
>>      coro_promise_type_found_p and refactored to cache the result.
>>      (coro_promise_type_found_p): Use the two functions above.
>>      (build_template_co_await_expr): New function.  Builds a
>>      CO_AWAIT_EXPR representing a CO_AWAIT_EXPR in a template
>>      declaration.
>>      (build_co_await): Use the above if processing_template_decl, and
>>      give it a propert type.
>>      (defer_expansion_p): New function.  Returns true iff its
>>      argument is a type-dependent expression OR the current functions
>>      traits class is type dependent.
>>      (finish_co_await_expr): Defer expansion only in the case
>>      defer_expasnion_p returns true.
>>      (finish_co_yield_expr): Ditto.
>>      (finish_co_return_stmt): Ditto.
>>      * pt.cc (type_dependent_expression_p): Do not treat
>>      CO_AWAIT/CO_YIELD specially.
>> gcc/testsuite/ChangeLog:
>>      PR c++/112341
>>      * g++.dg/coroutines/pr112341-2.C: New test.
>>      * g++.dg/coroutines/pr112341-3.C: New test.
>>      * g++.dg/coroutines/torture/co-yield-03-tmpl-nondependent.C: New
>>      test.
>>      * g++.dg/coroutines/pr112341.C: New test.
>> ---
>>   gcc/cp/coroutines.cc                          | 142 ++++++++++++++----
>>   gcc/cp/pt.cc                                  |   5 -
>>   gcc/testsuite/g++.dg/coroutines/pr112341-2.C  |  25 +++
>>   gcc/testsuite/g++.dg/coroutines/pr112341-3.C  |  65 ++++++++
>>   gcc/testsuite/g++.dg/coroutines/pr112341.C    |  21 +++
>>   .../torture/co-yield-03-tmpl-nondependent.C   | 140 +++++++++++++++++
>>   6 files changed, 361 insertions(+), 37 deletions(-)
>>   create mode 100644 gcc/testsuite/g++.dg/coroutines/pr112341-2.C
>>   create mode 100644 gcc/testsuite/g++.dg/coroutines/pr112341-3.C
>>   create mode 100644 gcc/testsuite/g++.dg/coroutines/pr112341.C
>>   create mode 100644 
>> gcc/testsuite/g++.dg/coroutines/torture/co-yield-03-tmpl-nondependent.C
>> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
>> index 127a1c06b56e..9494cb499454 100644
>> --- a/gcc/cp/coroutines.cc
>> +++ b/gcc/cp/coroutines.cc
>> @@ -85,6 +85,7 @@ struct GTY((for_user)) coroutine_info
>>     tree actor_decl;    /* The synthesized actor function.  */
>>     tree destroy_decl;  /* The synthesized destroy function.  */
>>     tree promise_type;  /* The cached promise type for this function.  */
>> +  tree traits_type;   /* The cached traits type for this function.  */
>>     tree handle_type;   /* The cached coroutine handle for this function.  */
>>     tree self_h_proxy;  /* A handle instance that is used as the proxy for 
>> the
>>                       one that will eventually be allocated in the coroutine
>> @@ -429,11 +430,12 @@ find_promise_type (tree traits_class)
>>     return promise_type;
>>   }
>>   +/* Perform initialization of the coroutine processor state, if not done
>> +   before.  */
>> +
>>   static bool
>> -coro_promise_type_found_p (tree fndecl, location_t loc)
>> +ensure_coro_initialized (location_t loc)
>>   {
>> -  gcc_assert (fndecl != NULL_TREE);
>> -
>>     if (!coro_initialized)
>>       {
>>         /* Trees we only need to create once.
>> @@ -466,6 +468,33 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
>>           coro_initialized = true;
>>       }
>> +  return true;
>> +}
>> +
>> +/* Try to get the coroutine traits class.  */
>> +static tree
>> +coro_get_traits_class (tree fndecl, location_t loc)
>> +{
>> +  gcc_assert (fndecl != NULL_TREE);
>> +
>> +  if (!ensure_coro_initialized (loc))
>> +    /* We can't continue.  */
>> +    return error_mark_node;
>> +
>> +  coroutine_info *coro_info = get_or_insert_coroutine_info (fndecl);
>> +  auto& traits_type = coro_info->traits_type;
>> +  if (!traits_type)
>> +    traits_type = instantiate_coro_traits (fndecl, loc);
>> +  return traits_type;
>> +}
>> +
>> +static bool
>> +coro_promise_type_found_p (tree fndecl, location_t loc)
>> +{
>> +  gcc_assert (fndecl != NULL_TREE);
>> +
>> +  if (!ensure_coro_initialized (loc))
>> +    return false;
>>       /* Save the coroutine data on the side to avoid the overhead on every
>>        function decl tree.  */
>> @@ -480,7 +509,7 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
>>         /* Get the coroutine traits template class instance for the function
>>       signature we have - coroutine_traits <R, ...>  */
>>   -      tree templ_class = instantiate_coro_traits (fndecl, loc);
>> +      tree templ_class = coro_get_traits_class (fndecl, loc);
>>           /* Find the promise type for that.  */
>>         coro_info->promise_type = find_promise_type (templ_class);
>> @@ -914,6 +943,19 @@ coro_diagnose_throwing_final_aw_expr (tree expr)
>>     return coro_diagnose_throwing_fn (fn);
>>   }
>>   +/* Build a co_await suitable for later expansion.  */
>> +
>> +tree
>> +build_template_co_await_expr (location_t kw, tree expr, tree type, tree 
>> kind)
>
> For consistency with other build*, let's put type before expr.

Sure, done.

>> +{
>> +  tree aw_expr = build5_loc (kw, CO_AWAIT_EXPR, type, expr,
>> +                         NULL_TREE, NULL_TREE, NULL_TREE,
>> +                         kind);
>
> Typically in templates we use build_min_nt_loc for the dependent case, and
> build_min_non_dep for the non-dependent case...though I suppose that
> distinction is primarily about handling TREE_SIDE_EFFECTS, which is less
> significant when we're always setting it to true anyway.  So I guess this is
> fine.

I don't think we can use the _nt variant here since the type is
variable, no?

>> +  TREE_SIDE_EFFECTS (aw_expr) = true;
>> +  return aw_expr;
>> +}
>> +
>> +
>>   /*  This performs [expr.await] bullet 3.3 and validates the interface 
>> obtained.
>>       It is also used to build the initial and final suspend points.
>>   @@ -922,11 +964,24 @@ coro_diagnose_throwing_final_aw_expr (tree expr)
>>       A, the original yield/await expr, is found at source location LOC.
>>         We will be constructing a CO_AWAIT_EXPR for a suspend point of one of
>> -    the four suspend_point_kind kinds.  This is indicated by SUSPEND_KIND.  
>> */
>> +    the four suspend_point_kind kinds.  This is indicated by SUSPEND_KIND.
>> +
>> +    In the case that we're processing a template declaration, we can't save
>> +    actual awaiter expressions as the frame type will differ between
>> +    instantiations, but we can generate them to type-check them and compute 
>> the
>> +    resulting expression type.  In those cases, we will return a
>> +    template-appropriate CO_AWAIT_EXPR and throw away the rest of the 
>> results.
>> +    Such an expression requires the original co_await operand unaltered.  
>> Pass
>> +    it as ORIG_OPERAND.  If it is the same as 'a', you can pass 
>> error_mark_node
>> +    (the default) to use 'a' as the value.  */
>
> Why error_mark_node rather than NULL_TREE as the default?  I'd prefer to
> reserve error_mark_node for actual errors.

Hm, unsure why I did that, and I see no reason to keep error_mark_node,
so I'll swap it out for NULL_TREE.

>>   static tree
>> -build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind)
>> +build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind,
>> +            tree orig_operand = error_mark_node)
>>   {
>> +  if (orig_operand == error_mark_node)
>> +    orig_operand = a;
>> +
>>     /* Try and overload of operator co_await, .... */
>>     tree o;
>>     if (MAYBE_CLASS_TYPE_P (TREE_TYPE (a)))
>> @@ -1131,11 +1186,13 @@ build_co_await (location_t loc, tree a, 
>> suspend_point_kind suspend_kind)
>>     if (REFERENCE_REF_P (e_proxy))
>>       e_proxy = TREE_OPERAND (e_proxy, 0);
>>   +  tree awrs_type = TREE_TYPE (TREE_TYPE (awrs_func));
>> +  tree suspend_kind_cst = build_int_cst (integer_type_node,
>> +                                     (int) suspend_kind);
>>     tree await_expr = build5_loc (loc, CO_AWAIT_EXPR,
>> -                            TREE_TYPE (TREE_TYPE (awrs_func)),
>> +                            awrs_type,
>>                              a, e_proxy, o, awaiter_calls,
>> -                            build_int_cst (integer_type_node,
>> -                                           (int) suspend_kind));
>> +                            suspend_kind_cst);
>>     TREE_SIDE_EFFECTS (await_expr) = true;
>>     if (te)
>>       {
>> @@ -1144,7 +1201,26 @@ build_co_await (location_t loc, tree a, 
>> suspend_point_kind suspend_kind)
>>         await_expr = te;
>>       }
>>     SET_EXPR_LOCATION (await_expr, loc);
>> -  return convert_from_reference (await_expr);
>> +
>> +  if (processing_template_decl)
>> +    /* FIXME: Can we reuse the results above rather than re-make at template
>> +       instantiating?  The "obvious" solution of checking if we have a fully
>> +       built expression and recurring into the operands and 'e' call
>> +       expressions seems to cause PR103868 again.  */
>
> In general all that we keep is types and name lookup results, everything else
> gets rebuilt at instantiation time, I wouldn't try to do anything different 
> for
> co_await.

OK.  I've dropped the FIXME.

>> +    return build_template_co_await_expr (loc, orig_operand, awrs_type,
>> +                                     suspend_kind_cst);
>> + return convert_from_reference (await_expr);
>> +}
>> +
>> +/* Returns true iff EXPR or the current functions traits class are 
>> dependent.
>> +   In those cases, we can't decide what the type of our expressions is, so 
>> we
>> +   defer expansion entirely.  */
>> +
>> +static bool
>> +defer_expansion_p (tree expr, location_t loc)
>
> This name doesn't suggest it's specific to coroutines.  Maybe 
> coro_dependent_p?

Yes, that's a better name, I've renamed it.

> I'm also skeptical about passing in loc here; we shouldn't need to give a
> diagnostic by this point.
> 
> Relatedly, it looks like none of the callers of coro_get_traits_class deal 
> with
> error_mark_node return.  If you aren't going to handle that, maybe
> coro_get_traits_class should abort instead of returning it.  Or call
> ensure_coro_initialized somewhere else.

Hm, I think a better design is indeed to extract the
ensure_coro_initialized call and make coro_initialized a precondition of
coro_dependent_p.  This means that coro_get_traits_class returns
NULL_TREE as the only possible error return.  I think I'll also extract
getting the traits class a level up so that the predicate is only busy
with checking whether the types are dependent, meaning there's no need
to contrive the promise_type being NULL_TREE into either a true or false
return from coro_dependent_p (currently true, because of
dependent_type_p (NULL_TREE) == true).

Working on those changes now.

Thanks for the comments.
-- 
Arsen Arsenović

Attachment: signature.asc
Description: PGP signature

Reply via email to