-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Wrong async return semantics #44395
Comments
Given that the expected behavior is depending on the runtime type this is likely a backend issue. The AST generated from CFE is
and I don't think we should do anything differently. |
I actually get a different output, based on
The complete output looks like this.
Here is the crucial part, as I see it:
The point is that |
The difference is due to the VM transformation applied. My example was with Would be valid to replace |
It's is typically valid to replace Future<int> f() {
return Future<num>.value(1) as dynamic;
} This should evaluate the returned expression, detect that it is not a subtype of So in the case where the returned expression has a type that guarantees that it will be a
Perhaps it could be a problem in the VM transformation as well as in the back-end processing done by dart2js? |
Given that it doesn't work in all cases we need backends to fix this. I think we should file issue for each backend and make this a meta issue. |
OK! |
I haven't checked whether DDC handles this correctly. |
@lrhn Is this (i.e. is auto-insertion of |
We always had an await for returned values in (Edit: This change was actually not introduced with null safety, it was already Sep 17 2018, in commit 5af9844.) But in this case the issue is that the await "leaks out of" the |
Test out for review here. |
Yes, it's intended because the alternative, leaking an error past a Looking at the type of the future was something we changed for Dart 2.0, when we stopped recursively flattening the type, because otherwise we had an unsound type system. It might not have been implemented at the time. There shouldn't be any change needed for functions where the (I really, really want to deprecate the ability to |
That is dart-lang/language#870 right? I'm also in favor of that. Would it be worth pushing on that instead of changing backend behavior? We could manage the breaking change with language versioning if we require the |
Cf. #44395. Change-Id: I753a6a4fae65c50267d8cf1575d2ce87e7ac345b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175064 Reviewed-by: Lasse R.H. Nielsen <lrn@google.com> Commit-Queue: Erik Ernst <eernst@google.com>
@natebosch @lrhn @johnniwinther the fix for this caused breakage internally. I think it's being fixed forward for now, but that does suggest that we may see additional runtime breakage in the future when folks opt in. Externally, opting in a package may be breaking in a way that folks are expecting. Do we still think this is sufficiently valuable (and sufficiently non-breaking) to try to land? If so, we should probably call this out explicitly in the CHANGELOG and possibly in a breaking change announcement. |
The impact wasn't very widespread - but more happened than I would have guessed. This also looks like it can break things in a really subtle way that is hard to track down. I lean towards reverting this and getter a better understanding of the impact before relanding or choosing to do something different. |
This got reverted and I think backends are still inconsistent with the spec, and we have tests in the SDK repo for the specced behavior. @lrhn @eernstg - is this something we should still push on? Should we put more attention towards dart-lang/language#870 instead? |
I can specify the current behavior, I just don't want to 😁. Doesn't matter, because it's bad semantics, and we should fix it. |
@lrhn wrote:
👍 |
@natebosch wrote (about adopting dart-lang/language#870):
I think dart-lang/language#870 is attractive because it allows us to simplify type inference for returned expressions, which may well provide a better developer experience (because type inference could succeed in some situations where it currently fails). However, dart-lang/language#870 is not only a change that will require the addition of In particular, any It is true that dart-lang/language#870 would eliminate the situation where a However, that would give rise to exactly the behavioral changes that the fix to this issue caused. In other words, the difficulties that arose when 14032a6 was landed must be handled in any case as part of the migration to dart-lang/language#870, so it's not like doing dart-lang/language#870 makes that issue go away. |
It would let us make those fixes incrementally instead of forcing them to be fixed before we release the change. Fixing the existing semantics without the static breaking change should be fine too. If we plan on doing that we might want to track on our OKRs. |
Appears to be consistently failing on all other backends. Re-enable this test if it is decided that this should be the correct behavior. Change-Id: If5016d23954748c0cb193f2f5faedb9f12820b5f Issue: #44395 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/214481 Reviewed-by: Nate Bosch <nbosch@google.com> Commit-Queue: Nicholas Shahan <nshahan@google.com>
I have added a skip for this test on the DDC firefox bot because it was flaking at a rate that was avoiding flake detection but consistently turning the bot red and requiring new approval d6db39e. We should remove the skip if we reland the change in the CFE or some other implemenation. |
As is, the `finally` block gets run before the future returned by the callback has completed. I believe the underlying issue here is this one in the Dart SDK: dart-lang/sdk#44395 The fix is to await the future and return the result of that, rather than returning the future directly. The Dart language folks (or at least some of them) hope to eventually deal with this by making the old code here a type error, requiring the value passed to `return` to have type T rather than Future<T>: dart-lang/language#870
@leafpetersen @eernstg @lrhn This needs an owner. We have a test which is failing for 3 years and nobody owns it. As I understand it - fixing this is not a problem (@johnniwinther had a patch), but rolling it out is complicated. So we need to make a decision here: we need to either commit to fixing it at some point or commit to never fixing it and change the spec instead. The current equilibrium where we just have a failing test and ignore it (because we can't see it) is not a good state. |
We could also introduce a warning for the situation where the wrong semantics occurs, instructing developers to replace The wrong semantics only occurs when the return statement is located inside a try/catch statement, and the evaluation of We don't immediately have a safe approximation of this situation based on the static type of This approach would make the semantics modification explicit for each location where it occurs, and developers would be in a rather good position to detect that the change from With this change in place (in "almost" all serious code out there), we're free to change the implementation such that the behavior is as specified. Note that By the way, we are also free to say that "this is wonderful! now we're halfway done with dart-lang/language#870!", because developers have already done some of the migration to that proposal, and in particular they've already done the kind of migration that is most likely to cause subtle bugs (which is exactly the insertion of |
Much as I hate to bring it up, inserting (But I'd also prefer to completely remove the await in async returns, and, honestly, change the behavior of await in general. Then this problem should go away, and hopefully a few more. But that's harder. Until then, just guiding people away from the problematic implementation should be easy.) |
I prefer to require |
Just want to check if we have concrete plan on how we would like to move this forward? |
It is on the agenda of the language team for tomorrow (it was there last week, too, but we should actually get around to it this time). |
The language team just decided that we'd like to explore the proposal in dart-lang/language#870, which implies that it will no longer be possible to return a future which must be implicitly awaited. (That is, With that in mind, it has low priority to eliminate the wrong semantics reported in this issue (that is: don't do it ;-). I've marked this issue as blocked (on dart-lang/language#870) to give yet another hint that it is dormant. Please use dart-lang/language#870 to follow the exploration of that proposal. This issue stays open in case the above mentioned exploration runs into serious difficulties. If dart-lang/language#870 works well then this issue will be moot and we can close it. |
Please, consider allowing the return of Future (or anything) without await for a method returning FutureOr to avoid disrupting optimizations. For use cases, see: |
The current behavior of It's quite possible that there is a synchronous completion of the returned future with a value, because that's what we do for normal value returns, because we can, but it's never been specified that that a return of a value from an So, don't assume synchronous completion, not even for returning a plain value. It's not guaranteed. |
I suspect fixing this issue may solve |
[Edit: This issue is blocked on dart-lang/language#870; see this comment for details.]
Consider the following program:
Execution of this program with
dart
(from 74be667, and 2.12.0-96.0.dev) gives rise to the following output:However, the semantics of
return f()
should have been as follows:(
$f$
is the function whose body contains the return statement)(In this case,
$o$
is an instance of a subtype ofFuture
whose type argument atFuture
isvoid
.)(
$T_v$
is the future value type of$f$
, that is,void
. And the subtype relation does hold.)The evaluation of said
await v
will throw, and hence we should proceed to execute theprint
statement and return fromg
by completing the body normally (and implicitly completing the returned future with the null object).The actual behavior indicates that no such evaluation of
await v
occurs, and the returned future is completed with the thrown exception, soawait g()
inmain
throws.In legacy code the await occurs at the same point, with similar spec language, since commit 5af9844 of Sep 17, 2018.
The text was updated successfully, but these errors were encountered: