(NOTICE: Written when the feature was called "inline class", is now "extension type", and is not completely the same. New conclusion too, probably, so check comments.)
TL;DR: flatten(N) of an inline class type N should be N.
What happens if you do await e and the static type of e is an inline class, N?
The runtime semantics are pretty clear, so it boils down to defining what flatten returns on an inline class.
(And it's probably going to be trivial, but let's make sure it's documented.)
As currently defined, an inline class can only implement other inline classes, so it cannot implement Future (or FutureOr).
It would seem valid to make flatten(N) = N for an inline-class type N, like we do for other types which do not imply an expected Future type.
At runtime, the inline class type does not exist, it's replaced entirely by the representation type.
(Basically, at runtime the inline class type can be treated as a type alias for the representation type, instead of a separate type. It needs to have all the same cyclic-reference restrictions as a type alias to avoid an infinite expansion when going from the static type to the runtime type.)
If N has representation type R, the static type of await e will be flatten(N), aka N, which is the same as R at runtime.
The await needs to do an is Future<R> check on the value of e to see if it should be, and can soundly be, awaited.
If the representation type is actually Future<int> itself, we won't await that future, because it's not a Future<Future<int>>.
That should work, and is probably what we want.
An alternative could be to looks at the representation type immediately, and see if it mentions a future type, and if so assume that we are awaiting that type.
I don't think it's a good idea. Mainly because it breaks the abstraction of the inline class, but also because it opens up questions about where R being a Future<R> should make the await e switch back to being typed as N or not.
So, let's not do that, the flatten(N) = N is simpler.
If, at a later time, we allow an inline class type to implement interfaces or other types (which must then also be supertypess of the representation type), then it may become possible for an inline class type to implement Future or FutureOr. At that point we should look at the future-behavior of the type itself, just as we do for other types. That should be sound wrt. the representation type, since it requires the representation type itself to have those types.
(NOTICE: Written when the feature was called "inline class", is now "extension type", and is not completely the same. New conclusion too, probably, so check comments.)
TL;DR: flatten(N) of an inline class type N should be N.
What happens if you do
await eand the static type ofeis an inline class, N?The runtime semantics are pretty clear, so it boils down to defining what flatten returns on an inline class.
(And it's probably going to be trivial, but let's make sure it's documented.)
As currently defined, an inline class can only implement other inline classes, so it cannot implement
Future(orFutureOr).It would seem valid to make flatten(N) = N for an inline-class type N, like we do for other types which do not imply an expected
Futuretype.At runtime, the inline class type does not exist, it's replaced entirely by the representation type.
(Basically, at runtime the inline class type can be treated as a type alias for the representation type, instead of a separate type. It needs to have all the same cyclic-reference restrictions as a type alias to avoid an infinite expansion when going from the static type to the runtime type.)
If N has representation type R, the static type of
await ewill be flatten(N), aka N, which is the same as R at runtime.The
awaitneeds to do anis Future<R>check on the value ofeto see if it should be, and can soundly be, awaited.If the representation type is actually
Future<int>itself, we won't await that future, because it's not aFuture<Future<int>>.That should work, and is probably what we want.
An alternative could be to looks at the representation type immediately, and see if it mentions a future type, and if so assume that we are awaiting that type.
I don't think it's a good idea. Mainly because it breaks the abstraction of the inline class, but also because it opens up questions about where R being a Future<R> should make the
await eswitch back to being typed as N or not.So, let's not do that, the flatten(N) = N is simpler.
If, at a later time, we allow an inline class type to implement interfaces or other types (which must then also be supertypess of the representation type), then it may become possible for an inline class type to implement
FutureorFutureOr. At that point we should look at the future-behavior of the type itself, just as we do for other types. That should be sound wrt. the representation type, since it requires the representation type itself to have those types.