================ @@ -9363,6 +9363,126 @@ Example: }]; } +def CoroAwaitSuspendDestroyDoc : Documentation { + let Category = DocCatFunction; + let Content = [{ + +The ``[[clang::coro_await_suspend_destroy]]`` attribute applies to an +``await_suspend(std::coroutine_handle<Promise>)`` member function of a +coroutine awaiter. When applied, suspensions into the awaiter use an optimized +call path that bypasses standard suspend intrinsics, and immediately destroys +the suspending coro. + +Instead of calling the annotated ``await_suspend()``, the coroutine calls +``await_suspend_destroy(Promise&)`` and immediately destroys the coroutine. + +Although it is not called, it is strongly recommended that `await_suspend()` +contain the following portability stub. The stub ensures the awaiter behaves +equivalently without `coro_await_suspend_destroy` support, and makes the +control flow clear to readers unfamiliar with the attribute: + +.. code-block:: c++ + + void await_suspend_destroy(Promise&) { /* actual implementation*/ } + [[clang::coro_await_suspend_destroy]] + void await_suspend(std::coroutine_handle<Promise> handle) { + // Stub to preserve behavior when the attribute is not supported + await_suspend_destroy(handle.promise()); + handle.destroy(); + } ---------------- snarkmaster wrote:
Okay, sounds like I need to make it very explicit. Let me put my lawyer hat on! Let me know if the following is good enough, to replace the lines you highlighted? --- Here is the formal contract for this attribute. The attribute is considered *active* when **both** of these are true: - The compiler supports it -- i.e. the macro `__has_cpp_attribute(clang::coro_await_suspend_destroy)` expands to a nonzero integer. - The `await_suspend` overload applicable to the current coroutine's promise type is annotated with `[[clang::coro_await_suspend_destroy]]`. If the attribute is **inactive**, then the compiler will follow the C++ standard suspension behavior. When `await_ready()` returns `false`: - First, the coroutine is suspended -- the compiler saves the coroutine state and creates a handle. - Then, `await_suspend` is invoked with the handle. - Note: With an inactive attribute, `await_suspend_destroy(Promise&)` may be defined, but is not part of the compiler's protocol. If the attribute is **active**, the compiler will follow this non-standard protocol whenever `await_ready()` returns `false`: - First, `await_suspend_destroy` is invoked with a mutable reference to awaiting coroutine's promise. - Then, the coroutine is immediately destroyed, as if on `co_return ...;` but without invoking either `return_void()` or `return_value()`. - Notes: * The coroutine is **not** suspended, and a handle is **not** created. * The applicable `await_suspend` is **not** called. It must still be declared, since the compiler looks for the attribute on this special member, but a definition is optional. NB: Before providing a definition, read the note on portability below. **Portability note:** It is strongly recommended to write your code in a way that does **not** rely on support for this attribute. Fortunately, the attribute's contract is designed so that portability does not require conditional compilation. Suppose you have the following standard `await_suspend`: ```cpp void await_suspend(std::coroutine_handle<MyPromise>& h) { record_suspension_via_promise(h.promise()); h.destroy(); } ``` Without loss of portability, you can replace it by `await_suspend_destroy`, plus a fallback `await_suspend`. Depending on the compiler, either one may be the entry point, but the behavior will be the same -- except for the speed, size, and allocation-elision benefits of the attribute. ```cpp // Entry point when `clang::coro_await_suspend_destroy` is supported void await_suspend_destroy(MyPromise& p) { record_suspension_via_promise(p); } // Entry point when `clang::coro_await_suspend_destroy` is not supported. // Emits no code when `clang::coro_await_suspend_destroy` is supported. void await_suspend(std::coroutine_handle<MyPromise>& h) { await_suspend_destroy(h.promise()); h.destroy(); } ``` The "standard" and "replacement" forms are equivalent because the fallback `await_suspend` replicates the attribute's contract, when the attribute is not supported by the compiler. **Warning:** Even if you only use Clang, do not neglect to add the portability stub -- LLVM reserves the right to remove support for this attribute in a later major release. https://github.com/llvm/llvm-project/pull/152623 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits