The core idea is that to make any trait with async methods to work with one future-like coroutine:
For all async methods in trait resulting object gets corresponding poll_* method;
At the first method call from target trait, we create a single (in a context for a given binding) instance of a state machine which contains all required state for all the async methods:
for each method there is one piece of state;
these pieces don't intersect => it's UB to poll a different method unless the current polled one is done.
Such state machines begin and end their lifecycle at terminal state:
Once created, they do nothing unless polled;
Given the contract described above has been held, after any of the poll_* methods returned Poll::Ready(..) these automatons proceed to their initial state;
In initial state, they must not hold references to self.
This way, we can safely use self between invokations of async methods, and also need to store only one state machine per entire trait, not per its method, also it gives us one associated type in desugaring.
So, we can store state machine's layout in vtable, and then have to "just" tell user a story on how to consume it.
From outside it should look like we consumed self, and got a future.
So I guess that in this case we make Pin<&mut Self> to appear as Pin<&move Self> in the desugared version of code.
And of course, our coroutine has to be dropped after that - there is no reason to keep it around.
When we call any of the methods of the trait, the type of returned future needs to somehow poll the state machine => we get a shim over mutable reference to an implicit(AFAIK) instance.
The place for this instance is yet to be decided, but the principle is that we want to return unsized value from the trait object.
Variants for baking storage include boxing, alloca, and inline storage; however, creation of the coroutine state is implicit (good syntax for specifying a place where to return a DST value is yet to be found).
Personally, my bet would use (even not RFCd yet) with clauses to provide capability ObjectPlace: Allocator for creating boxes inside of the referenced allocator, then caller themself decides how to store the object.
Returning an unsized value is a big problem. The idea of the inline async fn was to avoid returning an unsized value, so you brought back all the original problem with all the new problems this approach has (like for example not directly returning an owned Future)
Boxing may not be available on #![no_std] and I think it would be controversial to link a language feature as important as async in traits to the stdlib
alloca is nowhere near finished: it doesn't support dynamic alignment and can't work in async fns unless they themself begin returning unsized Futures
inline storage: I'm not sure what you mean by that but if it's in the parent struct then it's no different from the proposal you linked. So, what are you proposing in addition to that?
How would that work in a #![no_std] context with no allocator support?