-
Notifications
You must be signed in to change notification settings - Fork 205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"Incompatible with await" and async
return.
#3599
Comments
@johnniwinther @mraleph @rakudrama @osa1 The proposal is that an How complicated do you think this would be to implement, should we choose to go for it? We're very, very late in the release process, so it has to be really trivial to make it before the first patch. My suggestion is that the CFE marks the return as "not awaited" somehow. (Maybe a Then backends need to recognize this signal and skip the code to check-and-await a future value. |
@chloestefantsova How easy do you think the suggested CFE change will be? |
@johnniwinther We've just discussed this issue with @lrhn and concluded that the most work needs to be done by the backends. Currently, we don't instruct the backends to await on the return expressions, and we don't have any way to communicate to them that the await should be skipped. We certainly can add a boolean flag to the return statement that will inform the backends that the await can be skipped though, to simplify their part of work a bit. Do you think we should do that? |
Looking at the backend code with my untrained eyes, I think the ones I've checked may not be inserting any await code, and are relying on the flattening of That would make it hard to implement a per-return "don't await". (Or easy, it just have to pre-wrap in a Which all comes back to dart-lang/sdk#44395 - the implicit await is not implemented correctly, it happens after the return, not at the return. So, probably not going to do this. |
Proposal
For a
return e
in anasync
function, if the static type ofe
is incompatible with await, then the runtime semantics will never await. The static type ofe
must be directly assignable to the future value type of the function.Background
We've decided that a extension type which does not implement
Future
is incompatbile withawait
, which makes it an error toawait
it (or things bounds by that or made nullable, as many times as we wan't to wrap types up).It's not clear how that should affect
return
s in anasync
function which has an implicit (and optional)await
.Take just:
The type inference and specified behavior is to infer
Ext(someValue)
with context typeFutureOr<Ext>
,then at runtime check if the value implements
Future<Ext>
, and if so, await that future.Here
Ext
is an extension type which doesn't implementFuture
, so awaiting it is precisely what we prohibit forawait
.If
someValue
happens to implementFuture
, then using the currently specified behavior, it wll be awaited, blindy awaiting the representation value of an extension type which doesn't implementFuture
, which was what we wanted to prevent.Then the function body of the example above will not need to check whether the value is a
Future<Ext>
(aka,Future<Object?>
at runtime), and will not await anything if it is.Implementation issues
This requires the compilers to know that the type is incompatible with await, and support not awaiting it.
For the former, I believe an
async
functionreturn
from the CFE currently carries the future value type to check for (the flatten of the static type ofe
).If the getter for that future value type could be made nullable, a value of
null
would signal the compiler to not check orawait
in that particular return. Then the compiler won't need to do the "compatible with await" computation again.(If the backends delegate the awaiting to
_Future._complete
on the returned future of theasync
function, they should use_Future._completeWithValue
instead. Most likely the code already uses_completeWithValue
, since the future type to check for may not be the same as the future value type of the returned future.)Example
A concrete example where this could be relevant (similar to an extension type I have already written):
Using the current
async
return behavior would await the_ErrorHack
"future", no matter which future type is checked for, beause it implementsFuture<Never>
.And indeed:
This function fails to capture an error fomr the computation into a value, because when it tries to return that value, it's immediately recognized as a future and implicitly awaited, raising the error again as an error.
The returned future is completed with the error, not with the
Error
value.However, this is equally much a problem with using synchronous code and
Completer.complete
/Future.then
, because those functions also does the implicit await on any valid future. (That's why returns did it originally, they were implemented usingCompleter.complete
).So, we can't actually prevent such a non-future extension type carrying a future value from being awaited in a lot of situations.
And because of that,
has precisely the same behavior as the async version.
We can, and probably should, improve the APIs to have
Completer<T>.completeValue(T value)
,Future.map<R>(R Function(T), {R Function(Object, StackTrace)? onError})
and similar functions that ensure a value is passed through unmodified, even if it could potentially be a future. TheFutureOr<T>
type is a liability any timeT
can contain a value of typeFuture<T>
.For now, I think we should close the
return
loop-hole past "incompatbile with await" here, as described above. Usingasync
functions is what most people do, and getting that right is important. People usingFuture
methods and completers may have similar issues, but they are (even if just slightly) more sophisticated users, and may be able to understand the workarounds needed (which is currently wrapping the value in a future, which can be unwrapped instead of the future we want to preserve:Then work towards #870, to get rid of the implicit
await
, and add helper methods toFuture
andCompleter
which makes it possible to complete with aFuture
value that won't be awaited.The text was updated successfully, but these errors were encountered: