Skip to content

Commit 1dabfe6

Browse files
committed
Rework the AttrDocs.td addition based on feedback
1 parent 5d6a06d commit 1dabfe6

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
@@ -9278,10 +9278,10 @@ The ``[[clang::coro_await_suspend_destroy]]`` attribute may be applied to a C++
92789278
coroutine awaiter type. When this attribute is present, the awaiter must
92799279
implement ``void await_suspend_destroy(Promise&)``. If ``await_ready()``
92809280
returns ``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``
92969296
support, the awaiter will behave nearly identically, with the only difference
92979297
being 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

Comments
 (0)