@@ -9371,10 +9371,10 @@ The ``[[clang::coro_await_suspend_destroy]]`` attribute may be applied to a C++
93719371coroutine awaiter type. When this attribute is present, the awaiter must
93729372implement ``void await_suspend_destroy(Promise&)``. If ``await_ready()``
93739373returns ``false`` at a suspension point, ``await_suspend_destroy`` will be
9374- called directly, bypassing the ``await_suspend(std::coroutine_handle<...>)``
9375- method. The coroutine being suspended will then be immediately destroyed.
9374+ called directly. The coroutine being suspended will then be immediately
9375+ destroyed.
93769376
9377- Logically, the new behavior is equivalent to this standard code:
9377+ The new behavior is equivalent to this standard code:
93789378
93799379.. code-block:: c++
93809380
@@ -9389,10 +9389,24 @@ stub ``await_suspend()`` as above. Without ``coro_await_suspend_destroy``
93899389support, the awaiter will behave nearly identically, with the only difference
93909390being heap allocation instead of stack allocation for the coroutine frame.
93919391
9392- This attribute exists to optimize short-circuiting coroutines—coroutines whose
9393- suspend points are either (i) trivial (like ``std::suspend_never``), or (ii)
9394- short-circuiting (like a ``co_await`` that can be expressed in regular control
9395- flow as):
9392+ This attribute helps optimize short-circuiting coroutines.
9393+
9394+ A short-circuiting coroutine is one where every ``co_await`` or ``co_yield``
9395+ either immediately produces a value, or exits the coroutine. In other words,
9396+ they use coroutine syntax to concisely branch out of a synchronous function.
9397+ Here are close analogs in other languages:
9398+
9399+ - Rust has ``Result<T>`` and a ``?`` operator to unpack it, while
9400+ ``folly::result<T>`` is a C++ short-circuiting coroutine, with ``co_await``
9401+ acting just like ``?``.
9402+
9403+ - Haskell has ``Maybe`` & ``Error`` monads. A short-circuiting ``co_await``
9404+ loosely corresponds to the monadic ``>>=``, whereas a short-circuiting
9405+ ``std::optional`` coro would be an exact analog of ``Maybe``.
9406+
9407+ The C++ implementation relies on short-circuiting awaiters. These either
9408+ resume synchronously, or immediately destroy the awaiting coroutine and return
9409+ control to the parent:
93969410
93979411.. code-block:: c++
93989412
@@ -9404,7 +9418,20 @@ flow as):
94049418 return /* value representing the "execution short-circuited" outcome */;
94059419 }
94069420
9407- The benefits of this attribute are:
9421+ Then, a short-ciruiting coroutine is one where all the suspend points are
9422+ either (i) trivial (like ``std::suspend_never``), or (ii) short-circuiting.
9423+
9424+ Although the coroutine machinery makes them harder to optimize, logically,
9425+ short-circuiting coroutines are like syntax sugar for regular functions where:
9426+
9427+ - `co_await` allows expressions to return early.
9428+
9429+ - `unhandled_exception()` lets the coroutine promise type wrap the function
9430+ body in an implicit try-catch. This mandatory exception boundary behavior
9431+ can be desirable in robust, return-value-oriented programs that benefit from
9432+ short-circuiting coroutines. If not, the promise can always re-throw.
9433+
9434+ This attribute improves short-circuiting coroutines in a few ways:
94089435
94099436- **Avoid heap allocations for coro frames**: Allocating short-circuiting
94109437 coros on the stack makes code more predictable under memory pressure.
@@ -9423,7 +9450,7 @@ Here is a toy example of a portable short-circuiting awaiter:
94239450.. code-block:: c++
94249451
94259452 template <typename T>
9426- struct [[clang::coro_await_suspend_destroy]] optional_awaitable {
9453+ struct [[clang::coro_await_suspend_destroy]] optional_awaiter {
94279454 std::optional<T> opt_;
94289455 bool await_ready() const noexcept { return opt_.has_value(); }
94299456 T await_resume() { return std::move(opt_).value(); }
@@ -9437,21 +9464,6 @@ Here is a toy example of a portable short-circuiting awaiter:
94379464 }
94389465 };
94399466
9440- If all suspension points use (i) trivial or (ii) short-circuiting awaiters,
9441- then the coroutine optimizes more like a plain function, with 2 caveats:
9442-
9443- - **Behavior:** The coroutine promise provides an implicit exception boundary
9444- (as if wrapping the function in ``try {} catch { unhandled_exception(); }``).
9445- This exception handling behavior is usually desirable in robust,
9446- return-value-oriented programs that need short-circuiting coroutines.
9447- Otherwise, the promise can always re-throw.
9448-
9449- - **Speed:** As of 2025, there is still an optimization gap between a
9450- realistic short-circuiting coro, and the equivalent (but much more verbose)
9451- function. For a guesstimate, expect 4-5ns per call on x86. One idea for
9452- improvement is to also elide trivial suspends like `std::suspend_never`, in
9453- order to hit the `HasCoroSuspend` path in `CoroEarly.cpp`.
9454-
94559467}];
94569468}
94579469
0 commit comments