On Thu, Sep 11, 2025 at 7:21 AM Avi Kivity via Std-Proposals
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());".
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.
I suppose we could make "return;" work for a function returning a T that has a default constructor, but I'm not brave enough to propose it. There's also "return {};" that works equally well and is concise.
For coroutines, there are no concise alternatives, and the solution is deleting a line from the spec and a line from the compiler (or at least making a compiler check conditional on the supported standard version).