@@ -9278,10 +9278,10 @@ The ``[[clang::coro_await_suspend_destroy]]`` attribute may be applied to a C++
9278
9278
coroutine awaiter type. When this attribute is present, the awaiter must
9279
9279
implement ``void await_suspend_destroy(Promise&)``. If ``await_ready()``
9280
9280
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.
9283
9283
9284
- Logically, the new behavior is equivalent to this standard code:
9284
+ The new behavior is equivalent to this standard code:
9285
9285
9286
9286
.. code-block:: c++
9287
9287
@@ -9296,10 +9296,24 @@ stub ``await_suspend()`` as above. Without ``coro_await_suspend_destroy``
9296
9296
support, the awaiter will behave nearly identically, with the only difference
9297
9297
being heap allocation instead of stack allocation for the coroutine frame.
9298
9298
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:
9303
9317
9304
9318
.. code-block:: c++
9305
9319
@@ -9311,7 +9325,20 @@ flow as):
9311
9325
return /* value representing the "execution short-circuited" outcome */;
9312
9326
}
9313
9327
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:
9315
9342
9316
9343
- **Avoid heap allocations for coro frames**: Allocating short-circuiting
9317
9344
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:
9330
9357
.. code-block:: c++
9331
9358
9332
9359
template <typename T>
9333
- struct [[clang::coro_await_suspend_destroy]] optional_awaitable {
9360
+ struct [[clang::coro_await_suspend_destroy]] optional_awaiter {
9334
9361
std::optional<T> opt_;
9335
9362
bool await_ready() const noexcept { return opt_.has_value(); }
9336
9363
T await_resume() { return std::move(opt_).value(); }
@@ -9344,21 +9371,6 @@ Here is a toy example of a portable short-circuiting awaiter:
9344
9371
}
9345
9372
};
9346
9373
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
-
9362
9374
}];
9363
9375
}
9364
9376
0 commit comments