-
Notifications
You must be signed in to change notification settings - Fork 217
Specify null-safety async
returns
#941
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
Conversation
async
returns
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great feedback, thanks! Made various changes, PTAL.
Rethinking this a bit: The purpose of switching to a dynamic semantics built on "static await" criteria would be that programs are more comprehensible when it is specified directly that an await operation will definitely occur, or will definitely not occur. However, the rules turn out to be so complex that it might not help the reader of code in practice. So maybe we should simply keep the old semantics? It's somewhat quirky, and they do make it less predictable when there will be an await operation, but the old semantics is considerably simpler... |
As I mentioned in my review, I think we may need to use future value type in the dynamic semantics for reasons that I filed an issue about (unless I'm confusing myself in the late hour). I'm not convinced that the rest of the change to the dynamic semantics pulls its weight though. I think it's more breaking than advertised, and I don't see that it really makes anything clearer (if anything, less so). |
I agree, and I'm also worried about the breakage. So I'd suggest that we use the existing dynamic semantics, except for the 'future value type' soundness fix. |
I uploaded another commit where we use the old semantics, only adjusted to have the 'future value type' correction. |
My reason for preferring a static approach is not (just) simplicity, but lack of run-time overhead for type-checking - at least when it's possible. So, the static rule for
and the dynamic semantics are:
Those two match up, but I'd still prefer to change 2. to:
The only difference would occur when S <: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM. Can you write tests and file implementation issues?
a82edf6
to
47e1e3d
Compare
@lrhn wrote:
If |
This PR on the nnbd spec introduces the requirement "
S
assignable toT_v
orflatten(S) <: T_v
" (aka model 1) for a return statement in anasync
function, whereS
is the static type of the returned expression, andT_v
is the future value type of the function.This PR also specifies the dynamic semantics, using an approach where the presence/absence of an
await
step is statically determined whenever possible.The new dynamic semantics is the same as the pre-null-safety dynamic semantics (that is, the one which is currently specified in
dartLangSpec.tex
), except for the case where the returned object dynamic type is a subtype of as wellT_v
asFuture<T_v>
, and the returned object static type is notdynamic
, but it is a subtype ofT_v
. This case is expected to be very rare.@lrhn, I made some changes to the rules that you proposed: (1) The case where
S
isdynamic
is now first: This ensures that we will await aFuture<T_v>
, even in the case whereT_v
is a top type. If we checkS <: T_v
first then that case will apply, and we wouldn't await the future. The typical case isf() async => Future.value(1) as dynamic;
, and I think it is an unnecessary breaking change iff
would stop awaiting the future and instead use it to complete the returnedFuture<dynamic>
.(2) I removed the case for
S implements Future<dynamic>
, because that is a compile-time error with model 1 (because otherwise an earlier case would apply). (3) I removed the case forS is FutureOr<dynamic>
for the same reason.