-
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
Will need runtime check to cast to type (num) #24507
Comments
Simplified example: typedef void RequestAnimationFrameCallback(num highResTime);
int requestAnimationFrame(RequestAnimationFrameCallback callback) => 0;
void nextProgress(_) {
//...
requestAnimationFrame((x) {}); // no warning
requestAnimationFrame(nextProgress); // warning
} that's really strange. I don't know why the two cases are treated differently. |
@jmesserly The first example is handled via inference. I believe the second / main example would type check if |
I don't think we infer parameter types of lambdas? dart-archive/dev_compiler#203 |
but yeah, it does sound like a top -> void vs bottom -> void issue. I just don't see why a local function is different from a top level function. |
This is a limitation of our integration with the analyzer (at least so far). Our "fuzzy arrows", which are what allow limited use of "is" and "as" checks on function types, require us to treat unknown functions (function typed variables, tearoffs, etc) which have type typedef int Callback(dynamic x);
void foo(int f(int x)) {...};
void main() {
Callback f = ....;
f(3);
foo(f);
} There's an implied Now, in the original example above, typedef int Callback(dynamic x);
void foo(int f(int x)) {...};
void main() {
int f(dynamic x) => 3;
f(3);
foo(f);
} then there should be neither an implied This requires us to actually distinguish between the types |
yeah, that makes sense. Question about:
Consider this example: typedef int Callback(dynamic x);
void foo(int f(int x)) {...};
void main() {
((dynamic x) => 3)(3);
foo((dynamic x) => 3);
} It's now an anonymous function. Shouldn't it behave exactly like a named local function? In other words, there should neither be an implied |
Yes, that's right. Basically, we want to use the actual precise type ( There's a related issue with method override that we need to settle out as well. If you have a method of type class A {
void f(dynamic x) => ...
}
class B extends A {
// valid override
void f(int x) => ...
}
class C extends B {
// invalid since int </: bottom
void f(dynamic x) => ....
}
class D extends B {
// Ok
void f(Object x) => ...
} We could choose to allow the override in |
Another option might be to do the check callee side? I think a lot of folks tend to use class Base {
method(Object obj) {
}
}
class Derived {
// this method can't actually be called with anything other than Derived.
// method(Derived obj)
method(Object obj) {
var d = obj as Derived;
...
}
} That does mean Derived.method always has the covariant check, but Base.method no longer needs any checks. Also it matches how we handle generics. |
Stepping back, the rationale behind introducing the fuzzy arrows was that they are valid for use in is checks: since all functions of the appropriate arity are subtypes of them, the is check just serves as an arity/callability check, which seems to be the most common use case. They also allow for a few other use cases where you have a function typed parameter to which you want to be able to pass a function of any type. In order to support this without wrapping, we need to do the checks on the caller side since any function can be passed into a context expecting a fuzzy arrow (or else we need to do callee side checks all function parameters, which is one of the things we're trying to avoid). Now, we could do choose not to use fuzzy arrows for methods at all, only for functions. I think that would be fine (again, assuming that we reify fuzzy and non-fuzzy arrows appropriately during an early analyzer pass). We could also choose to support covariant overrides for methods, using callee side checks (I think this is what you are suggesting above)? This could be done either for any type, or only for overrides of Object. I'd kind of prefer not to go there though. |
Ah, I was only suggesting it for So concretely: I don't think we are marking (all?) methods with Also, does this imply that Object.== would need Line 59 in e928812
... it takes a dynamic parameter. I'm pretty sure we aren't checking that in our == implementation. Do we want to? That seems a high cost, when the most common thing to do is to override it with either Object or dynamic . (overriding it with anything else isn't really correct anyway...)
|
Yes, given our current override behavior, there is an implied dsend on all methods with dynamic argument types. I think I'm increasingly of the opinion though that at least for methods we ought to leave dynamic as top rather than bottom. We can still change it to bottom when do a tear-off, so we would get consistent behavior with functions. This does mean that we don't support the "pseudo-covariant" override pattern of overriding a method taking dynamic with one taking something concrete. I kind of feel thought that if we want to support that, we may want to have a different mechanism for it (e.g. explicitly marking the parameter position as covariant, with the resulting implied check). |
+1 to all of that |
Has this been fixed? I don't see a warning on @jmesserly 's example above. |
I still see a warning: typedef void RequestAnimationFrameCallback(num highResTime);
int requestAnimationFrame(RequestAnimationFrameCallback callback) => 0;
void nextProgress(_) {
//...
requestAnimationFrame((x) {}); // no warning
// [warning] nextProgress ((dynamic) → void) will need runtime check to cast to type (num) → void
requestAnimationFrame(nextProgress); // warning
} |
@vsmenon had another example: void foo(x) {
print(x);
}
void bar(void f(int x), int x) {
f(x);
}
void main() {
bar(foo, 42);
} |
The behavior is actually even worse now in some cases -- we will issue an "error". |
Can confirm that we never set fuzzyArrows to false in the current code. Chatted with Leaf, I'll take a shot at using fuzzyArrows:false from CodeChecker |
fix sent out for review: https://codereview.chromium.org/1700523002/ |
Lots of progress 👍 |
With
dartanalyzer --strong --lints web/index.dart
for this function
in
dart:html
With
_
I indicate that I don't use and don't intend to use the parameter. I also don't see why this should indicate a problem when the passed callback allows a more generic type then what the receiver requests.The text was updated successfully, but these errors were encountered: