Description
In Dart 1.0, dynamic is used as both top and bottom. In strong mode, we made it top almost everywhere, except that for function typed variables (only) we made dynamic on parameter types mean bottom in order to enable certain code patterns:
typedef Arity1(x);
typedef Arity2(x, y);
void test(Function f, List args) {
if (f is Arity1) { // Matches any 1 argument function
f(args[0]);
} else if (f is Arity2) { // Matches any 2 argument function
f(args[0], args[1]);
}
}
Since we how have a name for bottom in the language (Null), we can support this pattern directly:
typedef Arity1 = Function(Null);
typedef Arity2 = Function(Null, Null);
void test(Function f, List args) {
if (f is Arity1) { // Matches any 1 argument function
(f as dynamic)(args[0]);
} else if (f is Arity2) { // Matches any 2 argument function
(f as dynamic)(args[0], args[1]);
}
}
The original motivation for fuzzy arrows is gone, and it continues to cause user confusion and soundness difficulties. This is a tracking bug for the elimination of all remaining uses of them, following by the deprecation and elimination of the feature.
A hint has been added to the analyzer to warn on uses of this feature:
hint • A function of type '(int) → bool' can't be assigned to a variable of type '(dynamic) → bool' at /Users/leafp/tmp/test.dart:5:13 • uses_dynamic_as_bottom
This hint will become an error once code is fixed up appropriately.
An example:
bool listEqual(List a, List b, bool compare(x, y)) {...};
listEqual([1], [2], (int x, int y) => x == y);
This code will now cause a hint, since it uses a function taking two integer arguments in a context that might provide it with anything.
You can fix code like this by removing the types on the function or moving them into the body:
listEqual([1], [2], (_x, _y) { int x = _x; int y = _y; x == y;});
but it is often better to change the API to make the original call site valid:
bool listEqual<S, T>(List<S> a, List<T> b, bool compare(S x, T y)) {...}
listEqual([1], [2], (int x, int y) => x == y);