-
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
Generator functions element type issue #54159
Comments
Note that the observed behavior causes soundness issues: import "dart:async";
FutureOr<Iterable<int>?> foo() sync* {
yield 1;
yield 2;
yield 3;
}
main() {
var o = foo(); // `FutureOr<Iterable<int>?>`.
if (o is! Future<Object?>) {
// Not a future, so `o` is promoted to `Iterable<int>?`:
// o.length; // Error, confirms `?` in the type.
o?.length; // OK, confirms `Iterable` in the type.
o!; // Promote to `Iterable<int>`.
var xs = o.toList(); // Static type of `xs` is `List<int>`.
(xs as dynamic).add(true); // Succeeds -- it's not a `List<int>` after all.
print(xs.last.isEven); // Soundness issue: Calling `isEven` on `true`.
}
} |
Actually, it looks like this issue could be turned into a comment on #53052: That's already the implementation issue for the spec update that introduced the text which was quoted here. @chloestefantsova, it looks like something wasn't handled in 4352f10 after all? |
@eernstg I don't see how that's a CFE issue. The generated Kernel code looks ok to me. Do I miss anything?
|
If it isn't an CFE issue then it must be a backend issue, because it isn't a non-issue. ;-) Here's a variant of the example that shows (1) we have unsoundness, and (2) some tests do not reveal the unsoundness (presumably because they are translated into a no-op): import "dart:async";
FutureOr<Iterable<int>?> foo() sync* {
yield 1;
yield 2;
yield 3;
}
Object? id(Object? x) => x;
main() {
FutureOr<Iterable<int>?> o = foo() as dynamic;
print(o.runtimeType); // '_SyncStarIterable<dynamic>'.
print(o is Iterable<int>); // 'false'.
print(o is Iterable<int>?); // 'false'.
print(o is FutureOr<Iterable<int>?>); // "Unnecessary type check; the result is always 'true'", but prints 'false'.
o as FutureOr<Iterable<int>?>; // Succeeds.
print('Still running!');
id(o) as FutureOr<Iterable<int>?>; // Throws.
} The execution throws at the end, demonstrating that we can get Of course, the underlying issue is that About configurations: I see "Still running!" (that is, I see the bug) with |
@mkustermann, @sigmundch, WDYT? Can you explain how a function with return type I guess it's perfectly OK that this unsoundness isn't detected by the subsequent type checks because they can be compiled to no-ops when we have soundness. It just looks funny when we have "the result is always 'true'" from the analyzer and then 'false' at run time. ;-) |
cc @rakudrama It's quite possible this is an implementation bug - it doesn't appear like dart2js and ddc are using the type parameter extracted from the generator signature in their lowering for generator methods. In contrast, it does appear that they use it for |
Actually, we do seem to extract the type parameter when the type is not a Iterable<int>? g() sync* { yield 1; } Produces a |
Based on the logic here:
FutureOr return types when inferring the element type from sync* and async* functions.
|
cc @fishythefish - I don't believe this relates to #54311, but there is some overlap, so you may be interested in this as well. |
Ack. When I was fixing that issue, I was wondering if I should do something similar for the This is also an area of the compiler where we seem to always use |
https://dart-review.googlesource.com/c/sdk/+/341337 should fix this in dart2js, though it would be nice to get the element type directly from the CFE rather than having yet another implementation of the element type computation. @sgrekhov: https://github.com/dart-lang/co19/blob/master/Language/Functions/element_type_A02_t06.dart does not start passing, but I think that test is incorrect: |
@eernstg: What's the expected behavior of the two snippets you provided above? For the first one, dart2js previously showed unsoundness (by trying to invoke a nonexistent method) and now instead throws a For the second one, you said that
|
First, the entire semantics of Dart must be constrained to provide soundness. This means that whatever a program does, if it's not sound then we need to revisit the specified semantics to see if there's a bug in the specification, and the relationship between the specified semantics and the implemented semantics. In this case the implemented semantics differs from the specified semantics, and the result violates soundness. The specification was corrected recently by the introduction of the element type of a generator function. The type of the object returned from an invocation of a function whose body is In the example here we have a All other parts of the example are just probing the actual run-time type of the returned object as well as the treatment of the returned object during static analysis. Everything confirms the assumption that the static analysis trusts the declared return type (so we think we have a So there is really only one item in the list of expected (but not observed) behaviors: "
In this example the unsoundness consists in having a reference of type If dart2js has been changed such that we get a
You're right, that was sloppy. The bug is still the soundness violation created by The very last type check uses Again, I think everything should be working correctly (including the optimizations that turn But then we have this:
As long as we have this, the bug which is the core of this issue has not been fixed. I assume this is what you mean by 'incorrectly': The incorrect thing is that |
Thank you! Fixed by dart-lang/co19#2429 |
@eernstg: Thanks, in that case, I think dart2js is working correctly now. |
Previously we would encode the type of the value returned in `async` functions as the field `futureValueType` on `FunctionNode`. For all other kinds of functions, such as `sync`, `sync*`, and `async*`, that field would be null. This CL renames `futureValueType` into `emittedVAlueType`, and for functions of kinds `async`, `sync*`, and `async*` that is expected to be the type of values emitted via `return` or `yield` statements. For `sync` functions that field is supposed to contain `null`. In response to #54159 TEST=existing Change-Id: I1efdbcc4e75d150f5618c7ca50cfe49a0e54fce6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/341662 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Reviewed-by: Ömer Ağacan <omersa@google.com> Commit-Queue: Chloe Stefantsova <cstefantsova@google.com> Reviewed-by: Mayank Patke <fishythefish@google.com>
…c change Calculation of element type for the value returned from async/async*/sync* functions was recently adjusted in the spec to address soundness issue (dart-lang/language#3218). This change adjusts Dart VM to conform to the new spec. TEST=language/async/regression_54311_test TEST=co19/Language/Functions/element_type_* Fixes #54316 Issue #54311 Issue #54159 Change-Id: I4c51e7cba704d034350519375210bdb2086c5432 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/342082 Reviewed-by: Alexander Aprelev <aam@google.com> Reviewed-by: Erik Ernst <eernst@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
VM is updated for the changed language spec in 6b12473. Leaving this issue open as DDC and dart2wasm should be also updated. |
@alexmarkov wrote:
Thanks! I created separate issues for the subtasks: #54412 and #54413. |
Highlighting for visibility: https://dart-review.googlesource.com/c/sdk/+/341662 renamed |
Edit by eernstg: This issue is now an implementation issue for the soundness violation which is described below. We have the following remaining subtasks:
The following co19 tests fail on all runtime platforms
https://github.com/dart-lang/co19/blob/master/Language/Functions/element_type_A01_t03.dart
https://github.com/dart-lang/co19/blob/master/Language/Functions/element_type_A01_t04.dart
https://github.com/dart-lang/co19/blob/master/Language/Functions/element_type_A01_t06.dart
https://github.com/dart-lang/co19/blob/master/Language/Functions/element_type_A02_t03.dart
https://github.com/dart-lang/co19/blob/master/Language/Functions/element_type_A02_t04.dart
https://github.com/dart-lang/co19/blob/master/Language/Functions/element_type_A02_t06.dart
Example
According to the Dart specification
In fact, it looks like the element type of a generator function is always
dynamic
Tested on
Dart SDK version: 3.3.0-149.0.dev (dev) (Fri Nov 17 16:02:24 2023 -0800) on "windows_x64"
The text was updated successfully, but these errors were encountered: