@@ -9278,10 +9278,10 @@ The ``[[clang::coro_await_suspend_destroy]]`` attribute may be applied to a C++
92789278coroutine awaiter type. When this attribute is present, the awaiter must
92799279implement ``void await_suspend_destroy(Promise&)``. If ``await_ready()``
92809280returns ``false`` at a suspension point, ``await_suspend_destroy`` will be
9281- called directly, bypassing the ``await_suspend(std::coroutine_handle<...>)``
9282- method. The coroutine being suspended will then be immediately destroyed.
9281+ called directly. The coroutine being suspended will then be immediately
9282+ destroyed.
92839283
9284- Logically, the new behavior is equivalent to this standard code:
9284+ The new behavior is equivalent to this standard code:
92859285
92869286.. code-block:: c++
92879287
@@ -9296,10 +9296,24 @@ stub ``await_suspend()`` as above. Without ``coro_await_suspend_destroy``
92969296support, the awaiter will behave nearly identically, with the only difference
92979297being heap allocation instead of stack allocation for the coroutine frame.
92989298
9299- This attribute exists to optimize short-circuiting coroutines—coroutines whose
9300- suspend points are either (i) trivial (like ``std::suspend_never``), or (ii)
9301- short-circuiting (like a ``co_await`` that can be expressed in regular control
9302- flow as):
9299+ This attribute helps optimize short-circuiting coroutines.
9300+
9301+ A short-circuiting coroutine is one where every ``co_await`` or ``co_yield``
9302+ either immediately produces a value, or exits the coroutine. In other words,
9303+ they use coroutine syntax to concisely branch out of a synchronous function.
9304+ Here are close analogs in other languages:
9305+
9306+ - Rust has ``Result<T>`` and a ``?`` operator to unpack it, while
9307+ ``folly::result<T>`` is a C++ short-circuiting coroutine, with ``co_await``
9308+ acting just like ``?``.
9309+
9310+ - Haskell has ``Maybe`` & ``Error`` monads. A short-circuiting ``co_await``
9311+ loosely corresponds to the monadic ``>>=``, whereas a short-circuiting
9312+ ``std::optional`` coro would be an exact analog of ``Maybe``.
9313+
9314+ The C++ implementation relies on short-circuiting awaiters. These either
9315+ resume synchronously, or immediately destroy the awaiting coroutine and return
9316+ control to the parent:
93039317
93049318.. code-block:: c++
93059319
@@ -9311,7 +9325,20 @@ flow as):
93119325 return /* value representing the "execution short-circuited" outcome */;
93129326 }
93139327
9314- The benefits of this attribute are:
9328+ Then, a short-ciruiting coroutine is one where all the suspend points are
9329+ either (i) trivial (like ``std::suspend_never``), or (ii) short-circuiting.
9330+
9331+ Although the coroutine machinery makes them harder to optimize, logically,
9332+ short-circuiting coroutines are like syntax sugar for regular functions where:
9333+
9334+ - `co_await` allows expressions to return early.
9335+
9336+ - `unhandled_exception()` lets the coroutine promise type wrap the function
9337+ body in an implicit try-catch. This mandatory exception boundary behavior
9338+ can be desirable in robust, return-value-oriented programs that benefit from
9339+ short-circuiting coroutines. If not, the promise can always re-throw.
9340+
9341+ This attribute improves short-circuiting coroutines in a few ways:
93159342
93169343- **Avoid heap allocations for coro frames**: Allocating short-circuiting
93179344 coros on the stack makes code more predictable under memory pressure.
@@ -9330,7 +9357,7 @@ Here is a toy example of a portable short-circuiting awaiter:
93309357.. code-block:: c++
93319358
93329359 template <typename T>
9333- struct [[clang::coro_await_suspend_destroy]] optional_awaitable {
9360+ struct [[clang::coro_await_suspend_destroy]] optional_awaiter {
93349361 std::optional<T> opt_;
93359362 bool await_ready() const noexcept { return opt_.has_value(); }
93369363 T await_resume() { return std::move(opt_).value(); }
@@ -9344,21 +9371,6 @@ Here is a toy example of a portable short-circuiting awaiter:
93449371 }
93459372 };
93469373
9347- If all suspension points use (i) trivial or (ii) short-circuiting awaiters,
9348- then the coroutine optimizes more like a plain function, with 2 caveats:
9349-
9350- - **Behavior:** The coroutine promise provides an implicit exception boundary
9351- (as if wrapping the function in ``try {} catch { unhandled_exception(); }``).
9352- This exception handling behavior is usually desirable in robust,
9353- return-value-oriented programs that need short-circuiting coroutines.
9354- Otherwise, the promise can always re-throw.
9355-
9356- - **Speed:** As of 2025, there is still an optimization gap between a
9357- realistic short-circuiting coro, and the equivalent (but much more verbose)
9358- function. For a guesstimate, expect 4-5ns per call on x86. One idea for
9359- improvement is to also elide trivial suspends like `std::suspend_never`, in
9360- order to hit the `HasCoroSuspend` path in `CoroEarly.cpp`.
9361-
93629374}];
93639375}
93649376
0 commit comments