================
@@ -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

Reply via email to