It seems like we're on the same page. My point is that your last comment...
> in practice async=IO in most code
does not need to be true. We will never avoid the complexity of IO, but we can avoid the complexity of async, and virtual threads provide us that opportunity.
I didn't mean that = to be transitive. All async is IO, but obviously some IO is not async.
> We will never avoid the complexity of IO, but we can avoid the complexity of async
Because we can never avoid the complexity of IO, having a language construct that makes it obvious when you're using IO is valuable. IO code is different in kind than non-IO code, and having it explicitly marked helps keep the mess that is IO from propagating its problems throughout the rest of your code.
I don't entirely disagree that treating IO as something special at the linguistic level is a good idea, but I do think that async is a wildly indirect and confusing way to achieve that.
Language designers don't add async to make IO look special, they add it as a concurrent programming model.
We could use a different name for that concept to make it more clear why these ideas are related, like `IO`, but in practice async=IO in most code.