@@ -8258,15 +8258,23 @@ but do not pass them to the underlying coroutine or pass them by value.
8258
8258
def CoroAwaitElidableDoc : Documentation {
8259
8259
let Category = DocCatDecl;
8260
8260
let Content = [{
8261
- The ``[[clang::coro_await_elidable]]`` is a class attribute which can be applied
8262
- to a coroutine return type.
8261
+ The ``[[clang::coro_await_elidable]]`` is a class attribute which can be
8262
+ applied to a coroutine return type. It provides a hint to the compiler to apply
8263
+ Heap Allocation Elision more aggressively.
8263
8264
8264
- When a coroutine function that returns such a type calls another coroutine function,
8265
- the compiler performs heap allocation elision when the call to the coroutine function
8266
- is immediately co_awaited as a prvalue. In this case, the coroutine frame for the
8267
- callee will be a local variable within the enclosing braces in the caller's stack
8268
- frame. And the local variable, like other variables in coroutines, may be collected
8269
- into the coroutine frame, which may be allocated on the heap.
8265
+ When a coroutine function returns such a type, a direct call expression therein
8266
+ that returns a prvalue of a type attributed ``[[clang::coro_await_elidable]]``
8267
+ is said to be under a safe elide context if one of the following is true:
8268
+ - it is the immediate right-hand side operand to a co_await expression.
8269
+ - it is an argument to a ``[[clang::coro_await_elidable_argument]]`` parameter
8270
+ or parameter pack of another direct call expression under a safe elide context.
8271
+
8272
+ Do note that the safe elide context applies only to the call expression itself,
8273
+ and the context does not transitively include any of its subexpressions unless
8274
+ exceptional rules of ``[[clang::coro_await_elidable_argument]]`` apply.
8275
+
8276
+ The compiler performs heap allocation elision on call expressions under a safe
8277
+ elide context, if the callee is a coroutine.
8270
8278
8271
8279
Example:
8272
8280
@@ -8281,8 +8289,63 @@ Example:
8281
8289
co_await t;
8282
8290
}
8283
8291
8284
- The behavior is undefined if the caller coroutine is destroyed earlier than the
8285
- callee coroutine.
8292
+ Such elision replaces the heap allocated activation frame of the callee coroutine
8293
+ with a local variable within the enclosing braces in the caller's stack frame.
8294
+ The local variable, like other variables in coroutines, may be collected into the
8295
+ coroutine frame, which may be allocated on the heap. The behavior is undefined
8296
+ if the caller coroutine is destroyed earlier than the callee coroutine.
8297
+
8298
+ }];
8299
+ }
8300
+
8301
+ def CoroAwaitElidableArgumentDoc : Documentation {
8302
+ let Category = DocCatDecl;
8303
+ let Content = [{
8304
+
8305
+ The ``[[clang::coro_await_elidable_argument]]`` is a function parameter attribute.
8306
+ It works in conjunction with ``[[clang::coro_await_elidable]]`` to propagate a
8307
+ safe elide context to a parameter or parameter pack if the function is called
8308
+ under a safe elide context.
8309
+
8310
+ This is sometimes necessary on utility functions used to compose or modify the
8311
+ behavior of a callee coroutine.
8312
+
8313
+ Example:
8314
+
8315
+ .. code-block:: c++
8316
+
8317
+ template <typename T>
8318
+ class [[clang::coro_await_elidable]] Task { ... };
8319
+
8320
+ template <typename... T>
8321
+ class [[clang::coro_await_elidable]] WhenAll { ... };
8322
+
8323
+ // `when_all` is a utility function that composes coroutines. It does not
8324
+ // need to be a coroutine to propagate.
8325
+ template <typename... T>
8326
+ WhenAll<T...> when_all([[clang::coro_await_elidable_argument]] Task<T> tasks...);
8327
+
8328
+ Task<int> foo();
8329
+ Task<int> bar();
8330
+ Task<void> example1() {
8331
+ // `when_all``, `foo``, and `bar` are all elide safe because `when_all` is
8332
+ // under a safe elide context and, thanks to the [[clang::coro_await_elidable_argument]]
8333
+ // attribute, such context is propagated to foo and bar.
8334
+ co_await when_all(foo(), bar());
8335
+ }
8336
+
8337
+ Task<void> example2() {
8338
+ // `when_all` and `bar` are elide safe. `foo` is not elide safe.
8339
+ auto f = foo();
8340
+ co_await when_all(f, bar());
8341
+ }
8342
+
8343
+
8344
+ Task<void> example3() {
8345
+ // None of the calls are elide safe.
8346
+ auto t = when_all(foo(), bar());
8347
+ co_await t;
8348
+ }
8286
8349
8287
8350
}];
8288
8351
}
0 commit comments