P2583R2 — Symmetric Transfer and Sender Composition
Mungo Gill, Vinnie Falco
LEWG
C++20 provides symmetric transfer ([P0913R1](https://wg21.link/p0913r1)[1]) - a mechanism where `await_suspend` returns a `coroutine_handle<>` and the compiler resumes the designated coroutine as a tail call. Coroutine chains execute in constant stack space. `std::execution` ([P2300R10](https://wg21.link/p2300r10)[5]) composes asynchronous operations through sender algorithms. These algorithms create receivers that are structs, not coroutines. No `coroutine_handle<>` exists at any intermediate point in a sender pipeline. When a coroutine co_awaits a sender that completes synchronously, the stack grows by one frame per completion. [P3552R3](https://wg21.link/p3552r3)[2]’s `std::execution::task` inherits this property. A protocol-level fix exists: completion functions and `start()` return `coroutine_handle<>` instead of `void` , enabling struct receivers to propagate handles from downstream without becoming coroutines. The fix preserves zero-allocation composition but requires changing the return type of every completion function, every `start()` , and every sender algorithm in [P2300R10](https://wg21.link/p2300r10)[5]. This paper documents the gap, describes the fix, and enumerates