I'm not sure if someone discussed the idea before, but I'm not aware of such discussions in the AsyncDrop thread.
A problem with AsyncDrop is that a signature like follows:
trait AsyncDrop {
    fn poll_drop(self: Pin<&mut Self>, cx: &Context<'_>) -> Poll<()>;
}
requires the object to track whether poll_drop was called or whether the object was dropped internally and dynamically, and the implementor is obliged to "fuse" poll_drop manually.
However, there is a way to enforce typestate statically with a trick on lifetimes of mutable references! This means that code that attempts to call poll after poll_drop simply doesn't compile.
use std::marker::PhantomData;
struct DropOnce<'a> {
    _data: PhantomData<&'a mut ()>,
}
impl<'a> DropOnce<'a> {
    fn poll(&'a mut self) -> &'a mut Self {
        println!("poll");
        self
    }   
    fn start_drop(&'a mut self) -> Dropping<'a> {
        Dropping(self)
    }   
}
struct Dropping<'a>(&'a mut DropOnce<'a>);
impl<'a> Dropping<'a> {
    fn poll(&'a mut self) -> &'a mut Self {
        println!("dropping");
        self
    }   
}
fn main() {
    let mut x = &mut DropOnce { _data: PhantomData };
    for i in 0..3 { x = x.poll(); }
    let mut y = &mut x.start_drop();
    for i in 0..3 { y = y.poll(); }
    //x.poll(); // fails borrow checker
}
It works by fixing the lifetime 'a on the struct's signature. In this way, at most one instance of &'a mut Self can exist in the program at any time (&mut refs also have ownership), and if we consume it, it's gone. If we wrap it in Dropping, there is no one else who can retrieve it.
Based on this idea, I propose a new StronglyTypedFuture trait (intentionally ugly name) that encodes future states as typestates instead of runtime states, so that:
- The state of the future (whether it is running, completed or being dropped) is a part of the future's type
 - Futures are fused statically, and attempting to poll a future that has completed is a compiler error
 - Once a future is turned into a dropping future, attempting to poll the original future is a compiler error
 - Attempting to poll a dropping future that has completed is also a compiler error
 
enum StronglyTypedPoll<P, T> {
    Pending(P),
    Ready(T),
}
trait StronglyTypedFuture<'a> {
    type Output;
    fn poll<'b>(self: Pin<&'a mut Self>, cx: &Context<'b>) -> StronglyTypedPoll<Pin<&'a mut Self>, Self::Output>;
}
// Every other method need to ensure that the reference they get from &'b mut self
// matches 'a: 'b in order to ensure that such method cannot be called after a call to
// async_drop. This is currently enforced by the compiler if a field with lifetime 'a
// exists in the struct, but will not be the case if my proposal about expired references
// was implemented.
// (see https://internals.rust-lang.org/t/proposal-about-expired-references/11675/11)
trait StronglyTypedAsyncDrop<'a> {
    type DropFuture: StronglyTypedFuture<'a, Output = ()>;
    fn async_drop(self: Pin<&'a mut Self>) -> StronglyTypedFuture<'a, Output = ()>;
}