https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118074

--- Comment #7 from Weibo He <newsigma at 163 dot com> ---
Thanks, @Yanzuo Liu

It seems there are three issues with the bug:

1. [GCC 15 Regression]: Bad interaction with NRV for the trunk

2. [GCC 15 Regression]: Return object is being prematurely converted

Clang community resolved a similar issue:
https://github.com/llvm/llvm-project/issues/56532

https://godbolt.org/z/aE7jMxoE5

Expected Output(GCC 10 ~ 14):
    return_void
    final suspend
    A&&

Wrong Output(GCC 15):
    A&&
    return_void
    final suspend

3. [Undefined behaviour]: When should the result object is initialized?

(1) According to
CWG2563(https://github.com/GorNishanov/await/blob/master/2023_Issaquah/cwg2563-response.md),
for the third common pattern of coroutine:

> get_return_object returns a proxy, coroutine body executes, conversion to 
> return type happens when coroutine return the caller.
> Need coroutine body to execute before initialising the return-value since 
> coroutine body produces the result.

which says conversion occurs when a coroutine returns to the caller. Referring
to cppreference, we can conclude that conversion happens after await_suspend()
if and only if it returns to the caller/resumer.

> if await_suspend returns void, control is immediately returned to the 
> caller/resumer of the current coroutine (this coroutine remains suspended), 
> otherwise
> if await_suspend returns bool,
>     the value true returns control to the caller/resumer of the current 
> coroutine
>     the value false resumes the current coroutine.
> if await_suspend returns a coroutine handle for some other coroutine, that 
> handle is resumed (by a call to handle.resume()) (note this may chain to 
> eventually cause the current coroutine to resume).
> if await_suspend throws an exception, the exception is caught, the coroutine 
> is resumed, and the exception is immediately re-thrown.

Note that return_value() happens before final_suspend(), I consider the
behavior is well defined.

(2) Agree with your second point:

9.5.4 [dcl.fct.def.coroutine] paragraph 11 reads:

> The coroutine state is destroyed when control flows off the end of the 
> coroutine
> or the destroy member function ([coroutine.handle.resumption]) of a coroutine 
> handle ([coroutine.handle]) that refers to the coroutine is invoked.

If I am not misunderstanding, 'flows off the end of the coroutine' occurs
before 'returning to the caller'. It is unclear whether return value
initialization happens before or after destruction. And the wording 'flows off
the end of the coroutine' seems too strict. I suggest the following change:

> The coroutine state is destroyed when returns from the await_resume() of 
> final_suspend
> or the destroy member function ([coroutine.handle.resumption]) of a coroutine 
> handle ([coroutine.handle]) that refers to the coroutine is invoked.

Note that initialization will be done before destruction of coroutine state.

Maybe we should seperate this bug into three.

Reply via email to