-
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
Analyzer doesn't catch that method argument has wrong signature #30052
Comments
This is working as intended. Passing |
Isn't that why we have This is extremely confusing (especially that it only applies to methods). I don't see how the code above could ever be correct (it will always throw). I think we should change our intention here. If someone wants to write code like this they should have to explicitly opt-in the same way we do with accepting subclasses with |
I was under the impression it was Flutter that wanted strong-mode-implicit-downcasts disabled by default? |
No this isn't related to void foo(bool callback()) { }
class Bar {
void callFoo() {
foo(method); // <-- this call is illegal ("method" has the wrong signature)
}
void method() { }
}
class Baz extends Bar {
bool method() { return true; }
}
void main() {
new Baz().callFoo();
} Overriding An the override here means that the implicit downcast in If we have a closed world assumption (that is, we assume that we can see all of the classes that will ever be defined all at once), then we can know whether or not
|
That would be fine (well, sort of, it seems really dubious to me still) if @matanlurey It's a lot more nuanced than that. :-) I think most of the places where we want downcasts are now annotated with What I want personally is for the language to be intuitive, and I feel that the case here is a great example of unintuitive behavior. |
Agreed. My personal preference is to avoid all implicit downcasts, but I'm very open to identifying specific subsets of them that are especially pernicious. Do you have something systematic in mind here? Implicit downcasts of callbacks? Or just making the strong mode "impossible cast" warning fire for tearoffs as well, even though those aren't actually truly impossible casts? |
I'm not totally clear on what behavior you want to have. I see a couple of options for your example: 1. Don't report an error statically, and don't report an error at runtime. I don't think this is feasible because it violates soundness. Consider: void foo(bool callback()) {
bool b = callback(); // <--
}
class Bar {
void callFoo() {
foo(method);
}
void method() { }
}
class SubBar extends Bar {
int method() => 123;
}
void main() {
new SubBar().callFoo();
} On the marked line, 2. Don't report an error statically, but check it at runtime. This is what we do now. As Leaf noted, there are some ways the code could potentially be valid where the runtime check will succeed. Personally, I don't like that the static checker assumes this will succeed. I'd rather you get an error and require an explicit cast. I think you feel the same way? 3. Report an error statically. If we didn't do the implicit downcast, then you get an error here: void foo(bool callback()) { }
class Bar {
void callFoo() {
foo(method); // Can't assign a value of type () -> void to a parameter of type () -> bool.
}
void method() { }
} You could fix the error by doing a manual downcast: void foo(bool callback()) { }
class Bar {
void callFoo() {
foo(method as bool Function());
}
void method() { }
} Is that what you have in mind?
I don't follow how abstract affects things. Can you walk me through it? |
@munificent #3 is what I had in mind for this particular case. Abstractness matters only insofar as if the class was abstract, then it's possible the contract would be for the subclass to always override it? But even then it'd be pretty weird. @leafpetersen I would approach the problem the other way. Downcasts bad, except for specific cases. One of those cases is when an argument is marked as |
The problem comes from the fact that bool foo() => true;
void Function() bar = foo; // allowed
bool Function() baz = bar; // implicit down-cast that *will* succeed at runtime. The potentially unsafe down-cast is where strong mode puts the runtime-check. Now, an analyzer can be smarter than that. Leaf showed an example where the code works, but if the analyzer has global knowledge, it can know that no subclass of List<String> list = iterable as List<String>; |
Removing the ability to assign an Iterable (that doesn't implement List) to a variable of type List would be amazing. I make that mistake multiple times a day. |
@lrhn you misspelled
I'd be ok with adding an operator to succinctly cast to the context type, though you can already write this as a function:
I don't have any in mind, but as I say, I prefer to avoid implicit downcasts. For your
|
I just meant the current semantics (allowing subclassing to be covariant instead of only contravariant). |
There are a couple of things which haven't been mentioned in this discussion so far: (1) So let's consider this discussion to be about the types (2) Ian mentioned that since One special case is where an expression of type Another special case could be the original example where an instance of the We could introduce this new concept: "there exists a class If we were to do this then we would get diagnostic messages on many downcasts applied to Also note that this is not the same thing as saying that instance method tear-offs are considered to have an exact type (which Leaf mentioned that we could do), which would have a different set of trade-offs. |
Can you give an example of the |
Should this be considered as another request for #31410 Is there value in keeping both issues open? |
I think downcasts are being wholly re-addressed with the Null Safety type system. |
The analyzer doesn't catch this static type error:
Analyzer output:
VM output (checked mode):
The analyzer does catch the problem if I make
callFoo
andmethod
top-level functions or statics on the Bar class. It's only if they're methods that it's a problem.The text was updated successfully, but these errors were encountered: