-
Notifications
You must be signed in to change notification settings - Fork 226
Description
The goal is to catch more error-prone code during static analysis; for example, code which is guaranteed to error at runtime. This often two secondary effects: (1) reducing errors in developer understanding of such code, and (2) reducing dynamic dispatch in code where a developer did not think dynamic dispatch was occurring.
Here are some examples of such code:
fn(List<int> strings) => print(strings.first.isEven);
void main() {
var args = ["one", "two", "three"];
var useArgs = true;
fn(useArgs ? args : []);
}
Depending on the value of useArgs
, the last line is guaranteed to result in a runtime error. The surprise to the developer is that args
was even allowed on that line! The problem here is that the developer thought that inference would infer the type of []
from the type of args
. A "strict-inference" mode changes static analysis rules such that inference of []
does not fall back to dynamic, but fails.
Another common example that comes up on mailing lists is inference in Iterable.fold. Note the signature of Iterable<E>#fold<T>
:
T fold <T>(T initialValue, T combine(T previousValue, E element))
var a = [1, 2, 3].fold(true, (s, x) => s + x);
There are currently no compile-time errors in this code, but it will fail at runtime. A "strict-inference" mode would fail to infer the callback's type at compile-time. If the developer changes the type of a
to an explicit bool
, or adds an explicit type annotation to fold
, they will then see more clearly the error in their code.
The proposed "strict-inference" mode will report errors (e.g. Hints in analyzer) at the declaration of values whose types cannot be inferred. Here is an inexhaustive list (an exhaustive list will come with a more formal specification):
- a variable declared without a type (e.g. with
var
orfinal
) and no initializer. - a field declared without a type and no initializer.
- a function parameter declared without a type, excluding a method parameter whose types are inferred from a parent class (inherited? inferred?).
- an empty collection literals whose type(s) cannot be inferred from "above."
- an instantiation of a generic class, where the type argument(s) are neither explicitly provided, nor can be inferred from downwards inference or upwards inference, e.g.
var f = Future.error("Boo");
- an invocation of a generic function,where the type argument(s) are neither explicitly provided, nor can be inferred from downwards inference or upwards inference.
This issue is a continuation of dart-lang/sdk#33749. Thanks to @munificent, @leafpetersen, and @matanlurey for specifying most of the above.