@@ -9364,101 +9364,109 @@ Example:
93649364}
93659365
93669366def CoroAwaitSuspendDestroyDoc : Documentation {
9367- let Category = DocCatDecl ;
9367+ let Category = DocCatFunction ;
93689368 let Content = [{
93699369
9370- The ``[[clang::coro_await_suspend_destroy]]`` attribute may be applied to a C++
9371- coroutine awaiter type. When this attribute is present, the awaiter must
9372- implement ``void await_suspend_destroy(Promise&)``. If ``await_ready()``
9373- returns ``false`` at a suspension point, ``await_suspend_destroy`` will be
9374- called directly. The coroutine being suspended will then be immediately
9375- destroyed.
9370+ The ``[[clang::coro_await_suspend_destroy]]`` attribute applies to an
9371+ ``await_suspend(std::coroutine_handle<Promise>)`` member function of a
9372+ coroutine awaiter. When applied, suspensions into the awaiter use an optimized
9373+ call path that bypasses standard suspend intrinsics, and immediately destroys
9374+ the suspending coro.
93769375
9377- The new behavior is equivalent to this standard code :
9376+ Each annotated ``await_suspend`` member must contain a compatibility stub :
93789377
93799378.. code-block:: c++
93809379
9381- void await_suspend_destroy(YourPromise&) { ... }
9382- void await_suspend(auto handle) {
9380+ [[clang::coro_await_suspend_destroy]]
9381+ void await_suspend(std::coroutine_handle<Promise> handle) {
93839382 await_suspend_destroy(handle.promise());
93849383 handle.destroy();
93859384 }
93869385
9387- This enables `await_suspend_destroy()` usage in portable awaiters — just add a
9388- stub ``await_suspend()`` as above. Without ``coro_await_suspend_destroy``
9389- support, the awaiter will behave nearly identically, with the only difference
9390- being heap allocation instead of stack allocation for the coroutine frame.
9386+ An awaiter type may provide both annotated and non-annotated overloads of
9387+ `await_suspend()`, as long as each invocation of an annotated overload has a
9388+ corresponding `await_suspend_destroy(Promise&)` overload.
9389+
9390+ Instead of calling the annotated ``await_suspend()``, the coroutine calls
9391+ ``await_suspend_destroy(Promise&)`` and immediately destroys the coroutine
9392+ ``await_suspend_destroy()`` must return ``void`` (Note: if desired, it
9393+ would be straightforward to also support the "symmetric transfer"
9394+ `std::coroutine_handle` return type.)
9395+
9396+ This optimization improves code speed and size for "short-circuiting"
9397+ coroutines — those that use coroutine syntax **exclusively** for early returns
9398+ and control flow rather than true asynchronous operations.
9399+
9400+ Specifically, a short-circuiting awaiter is one that either proceeds
9401+ immediately (``await_ready()`` returns ``true``, skipping to
9402+ ``await_resume()``) or terminates the coroutine execution.
93919403
9392- This attribute helps optimize short-circuiting coroutines.
9404+ Then, a short-circuiting coroutine is one where **all** the awaiters (including
9405+ ``co_await``, ``co_yield``, initial, and final suspend) are short-circuiting.
93939406
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:
9407+ The short-circuiting coroutine concept introduced above has close analogs in
9408+ other languages:
93989409
93999410- 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 ``?``.
9411+ ``folly::result<T>`` is a C++ short-circuiting coroutine, within which
9412+ ``co_await or_unwind(someResult())`` acts just like ``someResult() ?``.
94029413
94039414- Haskell has ``Maybe`` & ``Error`` monads. A short-circuiting ``co_await``
94049415 loosely corresponds to the monadic ``>>=``, whereas a short-circuiting
94059416 ``std::optional`` coro would be an exact analog of ``Maybe``.
94069417
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:
9418+ Returning to C++, even non- short-circuiting coroutines, including asynchronous
9419+ ones that suspend, may contain short-circuiting awaiters, and those might still
9420+ see some performance benefit if annotated.
94109421
9411- .. code-block:: c++
9422+ Marking your ``await_suspend_destroy`` as ``noexcept`` can sometimes further
9423+ improve optimization.
94129424
9413- T val;
9414- if (awaiter.await_ready()) {
9415- val = awaiter.await_resume();
9416- } else {
9417- awaiter.await_suspend();
9418- return /* value representing the "execution short-circuited" outcome */;
9419- }
9425+ However, if **all** awaiters within a coroutine are short-circuiting, then the
9426+ coro frame **can reliably be allocated on-stack**, making short-circuiting
9427+ coros behave qualitatively more like plain functions -- with better
9428+ optimization & more predictable behavior under memory pressure.
94209429
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.
9430+ Technical aside: Heap elision becomes reliable because LLVM is allowed to elide
9431+ heap allocations whenever it can prove that the handle doesn't "escape" from
9432+ the coroutine. User code can only access the handle via suspend intrinsics,
9433+ and annotated short-circuiting awaiters simply don't use any.
94239434
9424- Although the coroutine machinery makes them harder to optimize, logically,
9425- short-circuiting coroutines are like syntax sugar for regular functions where :
9435+ Note that a short-circuiting coroutine differs in one important way from a
9436+ function that replaced each `co_await awaiter` with explicit control flow :
94269437
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:
9435-
9436- - **Avoid heap allocations for coro frames**: Allocating short-circuiting
9437- coros on the stack makes code more predictable under memory pressure.
9438- Without this attribute, LLVM cannot elide heap allocation even when all
9439- awaiters are short-circuiting.
9440-
9441- - **Performance**: Significantly faster execution and smaller code size.
9438+ .. code-block:: c++
94429439
9443- - **Build time**: Faster compilation due to less IR being generated.
9440+ T value;
9441+ if (awaiter.await_ready()) {
9442+ value = awaiter.await_resume();
9443+ } else {
9444+ // ... content of `await_suspend_destroy` ...
9445+ return /* early-termination return object */;
9446+ }
94449447
9445- Marking your ``await_suspend_destroy`` method as ``noexcept`` can sometimes
9446- further improve optimization.
9448+ That key difference is that `unhandled_exception()` lets the promise type wrap
9449+ the function body in an implicit try-catch. This automatic exception boundary
9450+ behavior can be desirable in robust, return-value-oriented programs that
9451+ benefit from short-circuiting coroutines. If not, the promise can re-throw.
94479452
9448- Here is a toy example of a portable short-circuiting awaiter:
9453+ Here is an example of a short-circuiting awaiter for a hypothetical
9454+ `std::optional` coroutine:
94499455
94509456.. code-block:: c++
94519457
94529458 template <typename T>
9453- struct [[clang::coro_await_suspend_destroy]] optional_awaiter {
9459+ struct optional_awaiter {
94549460 std::optional<T> opt_;
94559461 bool await_ready() const noexcept { return opt_.has_value(); }
94569462 T await_resume() { return std::move(opt_).value(); }
94579463 void await_suspend_destroy(auto& promise) {
9458- // Assume the return object of the outer coro defaults to "empty".
9464+ // The return object of `promise`'s coro should default to "empty".
9465+ assert(!promise.returned_optional_ptr_->has_value());
94599466 }
9460- // Fallback for when ` coro_await_suspend_destroy` is unavailable.
9467+ [[clang:: coro_await_suspend_destroy]]
94619468 void await_suspend(auto handle) {
9469+ // Fallback for when `coro_await_suspend_destroy` is unavailable.
94629470 await_suspend_destroy(handle.promise());
94639471 handle.destroy();
94649472 }
0 commit comments