Date: Fri, 12 Sep 2025 11:48:29 -0400
On Fri, Sep 12, 2025 at 11:40 AM Avi Kivity <avi_at_[hidden]> wrote:
>
> On Thu, 2025-09-11 at 17:27 -0400, Jason McKesson via Std-Proposals wrote:
>
> On Thu, Sep 11, 2025 at 7:21 AM Avi Kivity via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
>
>
> The Standard says
>
> If searches for the names return_void and return_value in the scope
>
> of the promise type each find any declarations, the program is ill-
> formed.
>
> However, there are cases where one wants both "co_return;" and
> co_return something;" statements in the same coroutine.
>
> The cases are when the coroutine returns a sum type such as std::future
> or std::expected, and one of the options is void (e.g.
> std::future<void> or std::expected<void, something>. In these cases,
> one would want
>
>
> co_return;
>
> to select the void branch of the sum type
>
> and
>
> co_return std::make_exception_ptr(...) // for std::future<void>
>
> or
>
> co_return something(...) // for std::expected<void, something>.
>
>
> to select the non-void branch.
>
> Early versions of gcc did not error when both return_value() and
> return_void() were defined; no problems were observed.
>
>
> From a conceptual perspective, `return` and `co_return` should behave
> similarly. As it currently stands, any function for which `return
> <expr>;` is valid is one where `return;` would not be valid. So it
> conceptually makes sense that `co_return` would behave the same way.
>
> That having been said, there's a reasonable argument to be made that
> the behavior of `co_return` is already fairly divergent from `return`.
> Of particular note is the fact that the relationship between the
> returned expression type (if any) and the function's apparent return
> type is not nearly as clear as it is for `return`. You may have to dig
> deeply into the coroutine machinery to figure out exactly what type
> you need to provide, if any.
>
> Also, there's the fact that coroutines can already have *multiple*
> different "return types": the type you can put into `co_return`. So
> you can already do `co_return X;` and `co_return Y;` where those two
> types have no conversion relationship between them or with some other
> third type. Adding `co_return;` to that list seems pretty reasonable,
> even if it does call a differently named function internally.
>
>
>
>
> Even for plain `return`, sum types make the case for both return; and return expr; in the same function.
>
> A function f that returns std::expected<T, E> can "return T();" or it can "return std::unexpected(E());".
That's made possible via implicit conversions with a known return
type. Try that in a deduced return function and it will fail.
> A function g that returns std::unexpected<void, E> cannot do the analogous and "return;" for the good case and "return std::unexpected(E());" for the bad case.
And it shouldn't. `return;` should mean "exit without returning a value".
>
> On Thu, 2025-09-11 at 17:27 -0400, Jason McKesson via Std-Proposals wrote:
>
> On Thu, Sep 11, 2025 at 7:21 AM Avi Kivity via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
>
>
> The Standard says
>
> If searches for the names return_void and return_value in the scope
>
> of the promise type each find any declarations, the program is ill-
> formed.
>
> However, there are cases where one wants both "co_return;" and
> co_return something;" statements in the same coroutine.
>
> The cases are when the coroutine returns a sum type such as std::future
> or std::expected, and one of the options is void (e.g.
> std::future<void> or std::expected<void, something>. In these cases,
> one would want
>
>
> co_return;
>
> to select the void branch of the sum type
>
> and
>
> co_return std::make_exception_ptr(...) // for std::future<void>
>
> or
>
> co_return something(...) // for std::expected<void, something>.
>
>
> to select the non-void branch.
>
> Early versions of gcc did not error when both return_value() and
> return_void() were defined; no problems were observed.
>
>
> From a conceptual perspective, `return` and `co_return` should behave
> similarly. As it currently stands, any function for which `return
> <expr>;` is valid is one where `return;` would not be valid. So it
> conceptually makes sense that `co_return` would behave the same way.
>
> That having been said, there's a reasonable argument to be made that
> the behavior of `co_return` is already fairly divergent from `return`.
> Of particular note is the fact that the relationship between the
> returned expression type (if any) and the function's apparent return
> type is not nearly as clear as it is for `return`. You may have to dig
> deeply into the coroutine machinery to figure out exactly what type
> you need to provide, if any.
>
> Also, there's the fact that coroutines can already have *multiple*
> different "return types": the type you can put into `co_return`. So
> you can already do `co_return X;` and `co_return Y;` where those two
> types have no conversion relationship between them or with some other
> third type. Adding `co_return;` to that list seems pretty reasonable,
> even if it does call a differently named function internally.
>
>
>
>
> Even for plain `return`, sum types make the case for both return; and return expr; in the same function.
>
> A function f that returns std::expected<T, E> can "return T();" or it can "return std::unexpected(E());".
That's made possible via implicit conversions with a known return
type. Try that in a deduced return function and it will fail.
> A function g that returns std::unexpected<void, E> cannot do the analogous and "return;" for the good case and "return std::unexpected(E());" for the bad case.
And it shouldn't. `return;` should mean "exit without returning a value".
Received on 2025-09-12 15:48:43