Skip to content

Commit 63cf306

Browse files
committed
Rework the AttrDocs.td addition based on feedback
1 parent 811501d commit 63cf306

File tree

1 file changed

+36
-24
lines changed

1 file changed

+36
-24
lines changed

clang/include/clang/Basic/AttrDocs.td

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9371,10 +9371,10 @@ The ``[[clang::coro_await_suspend_destroy]]`` attribute may be applied to a C++
93719371
coroutine awaiter type. When this attribute is present, the awaiter must
93729372
implement ``void await_suspend_destroy(Promise&)``. If ``await_ready()``
93739373
returns ``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``
93899389
support, the awaiter will behave nearly identically, with the only difference
93909390
being 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

Comments
 (0)