-
Notifications
You must be signed in to change notification settings - Fork 212
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Introduced "future value type" of async
functions; used it to correct return rules
#930
Conversation
…ct type rules about return statements
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.
Please don't land this, at least until we've discussed what the path forward is. If I understand this correctly, you're proposing a change to the pre-nnbd return rules? I don't know that we're planning to make any changes there. Or if this is intended to be an NNBD change, we need to land this in the NNBD feature spec as well (even if this is just in the form of a callout in the feature spec pointing to a diff to the formal spec). I also feel very strongly that we only have bandwidth for one round of changes around returns. We should aim to have:
- A single PR with the new rules.
- An accompanying set of tests for the changes .
I'm proposing that we fix the spec bug which makes the With that, I believe that this PR makes the pre-nnbd return rules for the relevant situation say what we intended to say all the time. Cf. the discussion in #914: If you both prefer model/option 1 then we should not put it into the language specification at this point, because that is a breaking change: import 'dart:async';
Future<num> f() {
Future f = Future<int>.value(2); // Or `FutureOr f = ..`.
return f;
}
void main() async {
print(await f());
} This program works in pre-null-safety Dart, but with model 1 it will be a compile-time error, and I'm not convinced that it is helpful for anybody to backport this breaking change. |
I don't think changing the pre-nnbd rules is a priority right now. |
I expect that the language specification with null-safety will rely on the existing language specification for thousands of details. So fixing a bug there serves both the direct purpose of fixing the bug in pre-nnbd Dart (and I understand that you think this is less important), but it also ensures that the specification is ready for null-safety. This ensures that the transition to null-safety will be well understood: If we wish to switch from model 2 ('S is assignable to T or flatten(S) is assignable to T') to model 1 ('S is assignable to T or flatten(S) <: T'), then that's what we will say in the nnbd spec. If we don't fix the spec bug now then we'll need to do it later, and it will be more difficult to understand what difference null-safety makes for |
I don't think that changing the pre-NNBD behavior is a priority. The NNBD behavior is more important because it doesn't work unless modified. That's still limited by not wanting to add too big changes to NNBD at this point. |
Just wondering, @lrhn, do you agree that the notion of the 'future value type' of an async function (or element type, or whatever we want to call it) clarifies and simplifies the rules overall? Also, do you agree that
I believe this PR describes the existing behavior of the CFE. The analyzer reports an error for the case that I mentioned (that is, We could decide by fiat that there is no implementation effort: The CFE is fine, and the analyzer behavior is a known bug (because it is a faithful implementation of a spec rule that didn't have exactly the intended properties). So we just decide that we don't intend to fix it before null-safety is introduced.
We can easily keep ' With that, and the rest of the specification switched to null-safety, I do believe that we have a working model. For the dynamic semantics, I don't see why we shouldn't be able to keep the current specification, if we prefer that. I proposed the 'extended static await' (xsa) rules in order to make all properties, and in particular the presence/absence of await, statically known whenever possible. I checked that it is a non-breaking change (except for that very marginal case where the returned expression So the xsa rules are a very-very-nearly non-breaking change, and I find the static nature of these rules helpful, in order to be able to tell developers what's going on. If we use I would still prefer to use the xsa rules because they are more static (and I think they make programs more predictable), but the difference is very small at that point. |
That's a good move, especially since it's not just flatten of the return type, as I initially assumed.
Yes. That's just wrong. That said, I don't particularly care whether we fix it in pre-NNBD code, as long as we fix it for post-NNBD code. We've lived with the pre-NNBD behavior for years now, and haven't gotten any serious complaints. Risking a breaking change is not worth it. So ...
Yes to that. The analyzer can fix the bug eventually, if they want to, but there is no urgency, but we'd have to specify the behavior they should fix it to (and not as "what the CFE does"). So, post-NNBD. I think I prefer "flatten(S) <: T" to "flatten(S) assignable to T". I prefer just "S assignable to T" even more, but fear it's too breaking (would require changes to the migration tool, and it's probably too late now, because it would require re-migrating already migrated code). The extended static await rule is interesting, though.
The tricky case is: Future<Future<int>> foo() async {
Future<dynamic> x = Future<int>.value(42);
return x;
} Here the static rules would use item 3. and await the That code currently works in our implementations, and it's allowed by the run-time semantics, so that would be the breaking change. I do like that the only implicit down-casts happen when the original static type contains The former will await any future, then down-cast the result to How about these "generalized static await" rules (to give them a name):
|
Here's a possible way forward: We land this as is (containing ' I'll create an implementation issue for the analyzer, give it the lowest priority, and explain that there is this discrepancy between the spec and the analyzer behavior, but we do not plan to fix it because pre-null-safety Dart will soon be in the past and we have lived with this behavior for years. Next, I'll create a PR on the nnbd spec: It will require the above phrase to be changed to ' How do you like that? |
Initial steps seem fine. The one thing I worry about wrt. NNBD semantics is that we change the run-time semantics in a way that cause new run-time errors. That is, the same programs are still accepted, but they have slightly different semantics in a few edge cases. That's still going to hit some people, and there won't be any good static help. |
We agreed to perform all the changes in null-safety world, so there will not be any changes to the language specification at this point (it happens in the nnbd spec). So I'll close this PR without landing it. |
Turns out that the existing rules did not correctly cover the following case:
The introduction of the future value type of an
async
function ensures that the situation gets one level of indirection simpler, and I used this simplification to solve the above problem.This also means that the rules about return statements in
async
functions have been changed such that they are no longer based on wrapping both the type of the expression whose value is used to complete the returned future, as well as the component type of the returned future (that is, the constraint on the possible instances used to complete that future). This is discussed in #914.