-
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
Disagreement about the necessity and behavior of a cast between analyzer and CFE/backends #52973
Comments
I'm also confused by the impact of adding an diff --git a/foo.dart b/foo.dart
index ebc2e52..923fdd8 100644
--- a/foo.dart
+++ b/foo.dart
@@ -3,7 +3,7 @@ class Foo<T> {}
class C<T extends Object> {
void something(T? Function() read) {
var argumentValue = read();
- if (argumentValue is! Foo<String>) {
+ if (argumentValue != null && argumentValue is! Foo<String>) {
return;
}
printStaticType(argumentValue); When I make this change and remove the cast, the call to The CFE thinks the variables have type |
The promotion from As such, the "unnecessary cast" warning is consistent, you get that warning for all up-casts, whether they affect type inference or not. If you don't do the cast, the static type of That's what's supposed to happen. I think. What it actually happening seems to be that the promoted type is That explains the behaviors. That intersection type is erased when the static type is captured, so it prints The call to We need to fix the invalid intersection types. Somehow. |
About the if (argumentValue == null || argumentValue is Foo<String>) ... We don't do promotion on "or" tests in general (we could here, the LUB is still a type of interest), so you get no promotion, and the type is still |
Interesting, @natebosch! I'd note that it is slightly dangerous to use functions like Here is the discrepancy again, in a slightly smaller example: // ignore_for_file: unused_local_variable
void f<X>(X? arg) {
if (arg is int) {
// Check that `arg` has type `X & int`.
X x = arg;
int i = arg;
// Show the run-time value of the erased static type of `arg`.
var xs = [arg];
print(xs.runtimeType); // `List<Object?>`.
// List<X> ys = xs; // CFE reports error, analyzer accepts.
List<X?> ys = xs; // Accepted by both.
}
}
void main() {
f<Object>(1);
} So the analyzer as well as the CFE accept However, when the list literal is created it gets the actual argument type Looks like the CFE uses the declared type of In any case, this issue should probably not use the label 'area-fe-analyzer-shared', considering that the CFE produces the surprising erasure, and the analyzer arrives at the expected result. (I wouldn't say 'correct'/'incorrect', because we don't have a detailed specification of how to erase intersection types in all relevant situations.) @johnniwinther, do you have further comments on this? |
Side note: this issue initiated an interesting discussion, which resulted in dart-lang/language#3239. |
There is a disagreement between the analyzer and the CFE (or the backends) around type promotion. My first instinct was that the analyzer was incorrect and the
unnecessary_cast
should not be reported, since the cast does impact behavior in the CFE.We can see the difference in behavior without the cast by printing the static type that was filled in by inference.
I also comparing the runtime behavior of printing the static type to the IDE hover type information.
This prints
Using hover information in the IDE, analyzer reports the type of
argumentValue
where it is printed asT & Foo<String>
, and reports the type offooValue
asFoo<String>
If I remove the "unnecessary" cast by running the quick fix, the behavior changes, and it prints:
This CFE behavior is a little confusing, because if
fooValue
is statically inferred asFoo<String>?
, there should have been an error at the call toprintStaticFooType
which takes a non-nullableFoo
.The text was updated successfully, but these errors were encountered: