Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: doc/STATIC_SAFETY.md

Issue 1541833002: Update strong mode doc (Closed) Base URL: https://github.com/dart-lang/dev_compiler.git@master
Patch Set: Fixed links Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« doc/RUNTIME_SAFETY.md ('K') | « doc/RUNTIME_SAFETY.md ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Strong Mode Static Checking
2
3 ## Overview
4
5 The Dart programming language has an optional, unsound type system. Although it is similar in appearance to languages such as Java, C#, or C++, its type system and static checking are fundamentally different. It permits erroneous behavior in ways that may be surprising to programmers coming from those and other tradi tionally typed languages.
Bob Nystrom 2016/02/02 23:02:17 "traditionally" -> "conventional"
vsm 2016/02/03 00:50:05 Done.
6
7 In Dart, static type annotations can be often misleading. Dart code such as:
8
9 ```dart
10 var list = ["hello", "world"];
11 List<int> listOfInts = list;
12 ```
13
14 produces neither static nor runtime errors. Actual errors may show up much late r on, e.g., with the following code, only at runtime on the invocation of `abs`:
15
16 ```dart
17 Iterable<int> iterableOfInts = listOfInts.map((i) => i.abs());
18 ```
19
20 Strong mode aims to catch such errors early by validating that variables - e.g., `listOfInts` - actually match their corresponding static type annotations - e.g ., `List<int>`. It constrains the Dart programming language to a subset of prog rams that type check under a restricted set of rules. It statically rejects exa mples such as the above.
21
22 To accomplish this, strong mode involves the following:
23
24 - Type inference. Dart’s standard type rules treat omitted types as `dynamic` and effectively suppresses any static errors or warnings on variables of this ty pe. Strong mode infers static types based upon context. In the example above, strong mode infers that `list` has type `List`. Note, in strong mode, programme rs may still explicitly use the `dynamic` type.
Bob Nystrom 2016/02/02 23:02:17 Maybe: "Dart’s standard type rules treats untyped
vsm 2016/02/03 00:50:05 Done.
25
26 - Strict subtyping. Dart’s primary sources of unsoundness are due to its subty ping rules on function types and generic classes. Strong mode restricts these: e.g., `List` may not used as `List<int>` in the example above. Note, strong doe s still preserve Dart's covariance of generic classes.
Bob Nystrom 2016/02/02 23:02:17 ping rules on function types and generic classes.
vsm 2016/02/03 00:50:04 We plug it with runtime checks, so it's effectivel
27
28 - Generic methods. Standard Dart does not yet provide generic methods. This m akes certain polymorphic methods difficult to use soundly. For example, the `Li st.map` invocation above is statically typed to return an `Iterable<dynamic>` in standard Dart. Strong mode allows methods to be annotated as generic. `List.ma p` is statically typed to return an `Iterable<T>` where `T` is bound to `int` at this use site. A number of common higher-order methods are annotated and check ed as generic in strong mode, and programmers may annotate their own methods as well.
Bob Nystrom 2016/02/02 23:02:17 "at this use site" -> "in the previous example".
vsm 2016/02/03 00:50:05 Done.
29
30 Strong mode is designed to work in conjunction with the Dart Dev Compiler (DDC), which uses static type verification to generate better code. DDC augments stro ng mode static checking with a minimal set of [runtime checks](RUNTIME_SAFETY.md ) that aim to provide full soundness of types.
31
32 Strong mode static analysis may also be used alone for stricter error checking.
33
34 Formal details of the strong mode type system may be found [here](https://dart- lang.github.io/dev_compiler/strong-dart.pdf).
35
36 ## Usage
37
38 Strong mode is now integrated into the Dart Analyzer. The analyzer may be invok ed in strong mode as follows:
39
40 $ dartanalyzer --strong myapp.dart
41
42 Strong mode may also be enabled in IDEs by creating (if necessary) an `.analysis _options` file in your project and appending the following entry to it:
43
44 ```
45 analyzer:
46 strong-mode: true
47 ```
48
49 ## Type Inference
50
51 An explicit goal of strong mode to provide stronger typing while preserving the terseness of Dart. Dart syntax and [common style](https://www.dartlang.org/effe ctive-dart/style/) encourages a modest level of type annotations. Strong mode should not compel programmers to add more. Instead, it relies on type inference .
Bob Nystrom 2016/02/02 23:02:17 How about replacing this with: "With strong mode,
vsm 2016/02/03 00:50:05 Done.
52
53 In Dart, per the specification, the static type of a variable `x` declared as:
54
55 ```dart
56 var x = <String, String>{ "hello": "world"};
57 ```
58
59 is `dynamic` as there is no explicit type annotation on the left-hand side. To d iscourage code bloat, the Dart style guide generally recommends omitting these t ype annotations in many situations. In these cases, the benefits of strong mode would be lost.
60
61 To avoid this, strong mode uses type inference. In the case above, strong mode infers and enforces the type of `x` as `Map<String, String>`. An important aspe ct to inference is ordering: when an inferred type may be used to infer another type. To maximize the impact, we perform the following inference:
62
63 - Top-level and static fields
64 - Instance fields and methods
65 - Local variables
66 - Allocation expressions
Bob Nystrom 2016/02/02 23:02:17 Maybe "Constructor calls and collection literals"?
vsm 2016/02/03 00:50:05 Done, but dropped "collection" - closure literals
67 - Generic method invocations
68
69 Inference may tighten the static type as compared to the Dart specification. Th e `dynamic` type, either alone or in the context of a function or generic parame ter type, is inferred to a more specific type. This inference may result in str icter type errors than standard Dart.
Bob Nystrom 2016/02/02 23:02:17 I wouldn't code format "dynamic" since a reader ma
vsm 2016/02/03 00:50:04 Done.
70
71 In [DDC](RUNTIME_SAFETY.md), inference may also affect the reified runtime type.
72
73 ### Top-level and Static Fields
74
75 Strong mode will infer the static type of any top-level or static field with:
Bob Nystrom 2016/02/02 23:02:17 "will infer" -> "infers" Present tense is your fr
vsm 2016/02/03 00:50:05 Done.
76
77 - No static type annotation
78 - An initializer expression
79
80 The static type of the declared variable is inferred as the static type of the i nitializer. For example, consider:
81
82 ```dart
83 var PI = 3.14159;
84 var TAU = PI * 2;
85 ```
86
87 Strong mode would infer the static type of `PI` as `double` directly from its in itializer. It would infer the static type of `TAU` as `double`, transitively us ing `PI`’s inferred type. Standard Dart rules would treat the static type of bot h `PI` and `TAU` as `dynamic`. Note that the following later assignment would b e allowed in standard Dart, but disallowed (as a static type error) in strong mo de:
Bob Nystrom 2016/02/02 23:02:17 "would infer" -> "infers".
vsm 2016/02/03 00:50:05 Done.
88 ```dart
89 PI = "\u{03C0}"; // Unicode string for PI symbol
Bob Nystrom 2016/02/02 23:02:17 This might be a weird example since users expect P
vsm 2016/02/03 00:50:05 Done
90 ```
91 Strong mode inference avoids circular dependences. If a variable’s initializer expression refers to another variable whose type would be dependent (directly or transitively) on the first, the static type of that other variable is treated a s `dynamic` for the purpose of inference. In this modified example,
Bob Nystrom 2016/02/02 23:02:17 "transitively" -> "indirectly".
vsm 2016/02/03 00:50:05 Done.
92
93 ```dart
94 var _PI_FIRST = true;
95 var PI = _PI_FIRST ? 3.14159 : TAU / 2;
96 var TAU = _PI_FIRST ? PI * 2 : 6.28318;
97 ```
98
99 the variables `PI` and `TAU` are circularly dependent on each other. Strong mod e would leave the static type of both as `dynamic`.
Bob Nystrom 2016/02/02 23:02:17 Also kind of a strange example. Maybe just leave t
vsm 2016/02/03 00:50:05 Took the examples out.
100
101 ### Instance Fields and Methods
102
103 Strong mode performs two types of inference on instances fields and methods.
Bob Nystrom 2016/02/02 23:02:17 "instances" -> "instance".
vsm 2016/02/03 00:50:05 Done.
104
105 The first uses base types to constrain overrides in subtypes. Consider the foll owing example:
106
107 ```dart
108 abstract class A {
109 Map get m;
110 int value(int i);
111 }
112
113 class B extends A {
114 var m;
115 value(i) => m[i];
116
117 }
118 ```
119
120 In Dart, overridden method, getter, or setter types should be subtypes of the co rresponding base class ones (otherwise, static warnings are given). In standard Dart, the above declaration of `B` is not an error: both `m`’s getter type and `value`’s return type are `dynamic`.
121
122 Strong mode - without inference - would disallow this: if `m` in `B` could be as signed an arbitrarily typed value, it would violate the type contract in the dec laration of `A`.
Bob Nystrom 2016/02/02 23:02:17 " - " -> "—". "an arbitrarily typed value" -> "an
vsm 2016/02/03 00:50:05 Done.
123
124 However, rather than rejecting the above code, strong mode employs inference to tighten the static types to obtain a valid override. The corresponding types in B are inferred as if it was:
125
126 ```dart
127 class B extends A {
128 Map m;
129 int value(int i) => m[i];
130
131 }
132 ```
133
134 Note that tightening the argument type for `i` to `int` is not required for soun dness; it is done for convenience as it is the typical intent. The programmer m ay explicitly type this as `dynamic` or `Object` to avoid inferring the narrower type.
135
136 The second form inference is limited to instance fields (not methods) and is sim ilar to that on static fields. For instance fields where the static type is omi tted and an initializer is present, the field’s type is inferred as the initiali zer’s type. In this continuation of our example:
137
138 ```dart
139 class C extends A {
140 var y = 42;
141 var m = <int, int>{ 0: 38};
142 ...
143 }
144 ```
145
146 the instance field `y` has inferred type `int` based upon its initializer. Note that override-based inference takes precedence over initializer-based inference . The instance field `m` has inferred type `Map`, not `Map<int, int>` due to th e corresponding declaration in `A`.
147
148 ### Local Variables
149
150 As with fields, local variable types are inferred if the static type is omitted and an initializer expression is present. In the following example:
151
152 ```dart
153 Object foo(int x) {
154 final y = x + 1;
155 var z = y * 2;
156 return z;
157 }
158 ```
159
160 the static types of `y` and `z` are both inferred as `int` in strong mode. Note that local inference is done in program order: the inferred type of `z` is comp uted using the inferred type of `y`. Local inference may result in strong mode t ype errors in otherwise legal Dart code. In the above, a second assignment to ` z` with a string value:
161 ```dart
162 z = “$z”;
Bob Nystrom 2016/02/02 23:02:17 Unsmarten the quotes.
vsm 2016/02/03 00:50:05 Done.
163 ```
164 would trigger a static error in strong mode, but is allowed in standard Dart. I n strong mode, the programmer must use an explicit type annotation to suppress i nference. Explicitly declaring `z` with the type `Object` or `dynamic` would su ffice in this case.
Bob Nystrom 2016/02/02 23:02:17 Maybe "would" -> "This would".
vsm 2016/02/03 00:50:05 This is continuing the sentence before the code sn
165
166 ### Allocation Expressions
167
Bob Nystrom 2016/02/02 23:02:17 I think it would be good to define "allocation exp
vsm 2016/02/03 00:50:04 Changed to match the list above.
168 Strong mode also performs contextual inference on allocation expressions. This inference is rather different from the above: it tightens the runtime type of th e corresponding expression using the static type of its context. Contextual inf erence is used on expressions that allocate a new object: closure literals, map and list literals, and explicit constructor invocations (i.e., via `new` or `con st`).
169
170 In DDC, these inferred types are also [reified at runtime](RUNTIME_SAFETY.md) on the newly allocated objects to provide stronger runtime type safety.
Bob Nystrom 2016/02/02 23:02:17 I think most users think of "type safety" as a com
vsm 2016/02/03 00:50:04 How about "stronger soundness guarantee"?
171
172 #### Closure literals
173
174 Consider the following example:
175
176 ```dart
177 int apply(int f(int arg), int value) {
178 return f(value);
179 }
180
181 void main() {
182 int result =
183 apply((x) { x = x * 9 ~/ 5; return x + 32; }, 41);
184 print(result);
185 }
186 ```
187
188 The function `apply` takes another function `f`, typed `(int) -> int`, as its fi rst argument. It is invoked in `main` with a closure literal. In standard Dart , the static type of this closure literal would be `(dynamic) -> dynamic`. In s trong mode, this type cannot be safely converted to `(int) -> int` : it may retu rn a `String` for example.
189
190 Dart has a syntactic limitation in this case: it is not possible to statically a nnotate the return type of a closure literal.
191
192 Strong mode sidesteps this difficulty via contextual inference. It infers the c losure type as `(int) -> int`. Note, this may trigger further inference and typ e checks in the body of the closure.
193
194 #### List and map literals
195
196 Similarly, strong mode infers tighter runtime types for list and map literals. E.g., in
197
198 ```dart
199 List<String> l = [ "hello", "world" ];
Bob Nystrom 2016/02/02 23:02:17 "l" -> "list" or maybe "words".
vsm 2016/02/03 00:50:05 Done.
200 ```
201
202 the runtime type is inferred as `List<String>` in order to match the context of the left hand side. In other words, the code above type checks and executes as if it was:
203
204 ```dart
205 List<String> l = <String>[ "hello", "world" ];
206 ```
207
208 Similarly, the following will now trigger a static error in strong mode:
209
210 ```dart
211 List<String> l = [ "hello", 42 ]; // Strong Mode Error: 42 is not a String
212 ```
213
214 Contextual inference may be recursive:
215
216 ```dart
217 Map<List<String>, Map<int, int>> map =
218 { ["hello"]: { 0: 42 }};
219 ```
220
221 In this case, the inner map literal is inferred as a `Map<int, int>`. Note, str ong mode will statically reject code where the contextually required type is not compatible. This will trigger a static error:
222
223 ```dart
224 Map<List<String>, Map<int, int>> map =
225 { ["hello"]: { 0: "world" }}; // STATIC ERROR
226 ```
227
228 as "world" is not of type `int`.
229
230 #### Constructor invocations
231
232 Finally, strong mode performs similar contextual inference on explicit construct or invocations via new or const. For example:
Bob Nystrom 2016/02/02 23:02:17 Code format "new" and "const"?
vsm 2016/02/03 00:50:05 Done.
233
234 ```dart
235 Set<String> string = new Set.from(["hello", "world"]);
236 ```
237
238 is treated as if it was written as:
239
240 ```dart
241 Set<String> string =
242 new Set<String>.from(<String>["hello", "world"]);
243 ```
244
245 Note, as above, context is propagated downward into the expression.
246
247 ## Strict subtyping
248
249 The primary sources of unsoundness in Dart are generics and functions. Both int roduce circularity in the Dart subtyping relationship.
250
251 ### Generics
252
253 Generics in Dart are covariant, with the added rule that the `dynamic` type may serve as both ⊤ (top) and ⊥ (bottom) of the type hierarchy in certain situations . For example, let *<:<sub>D</sub>* represent the standard Dart subtyping rule . Then, for all types `S` and `T`:
254
255 `List<S>` <:<sub>D</sub> `List<dynamic>` <:<sub>D</sub> `List<T>`
256
257 where `List` is equivalent to `List<dynamic>`. This introduces circularity - e. g.:
258
259 `List<int>` <:<sub>D</sub> `List` <:<sub>D</sub> `List<String>`<:<sub>D</sub> `L ist` <:<sub>D</sub> `List<int>`
260
261 From a programmer’s perspective, this means that, at compile-time, values that a re statically typed `List<int>` may later be typed `List<String>` and vice versa . At runtime, a plain `List` can interchangeably act as a `List<int>` or a `Lis t<String>` regardless of its actual values.
262
263 Our running example exploits this. A `MyList` may be passed to the `info` funct ion as it’s a subtype of the expected type:
264
265 `MyList` <:<sub>D</sub> `List` <:<sub>D</sub>`List<int>`
266
267 In strong mode, we introduce a stricter subtyping rule <:<sub>S</sub> to disallo w this. In this case, in the context of a generic type parameter, dynamic may o nly serve as ⊤. This means that this is still true:
268
269 `List<int>` <:<sub>S</sub> `List`
270
271 but that this is not:
272
273 `List<int>` ~~<:<sub>S</sub> `List`~~
274
275 Our running example fails in strong mode:
276
277 `MyList` <:<sub>S</sub> `List` ~~<:<sub>S</sub> `List<int>`~~
278
279
280 ### Functions
281
282 The other primary source of unsoundness in Dart is function subtyping. An unusu al feature of the Dart type system is that function types are bivariant in both the parameter types and the return type (see Section 19.5 of the [Dart specifica tion](http://www.google.com/url?q=http%3A%2F%2Fwww.ecma-international.org%2Fpubl ications%2Ffiles%2FECMA-ST%2FECMA-408.pdf&sa=D&sntz=1&usg=AFQjCNGoFPzBNx2fgejKQg SgiS2dUBstBw)). As with generics, this leads to circularity:
Jennifer Messerly 2016/02/02 23:07:45 suggestion, direct link: http://www.ecma-internati
vsm 2016/02/03 00:50:04 Done.
283
284 `(int) -> int` <:<sub>D</sub> `(Object) -> Object` <:<sub>D</sub> `(int) -> int`
285
286 And, as before, this can lead to surprising behavior. In Dart, an overridden me thod’s type should be a subtype of the base class method’s type (otherwise, a st atic warning is given). In our running example, the (implicit) `MyList.length` getter has type:
287
288 `() -> Object`
289
290 while the `List.length` getter it overrides has type:
291
292 `() -> int`
293
294 This is valid in standard Dart as:
295
296 `() -> Object` <:<sub>D</sub> `() -> int`
297
298 Because of this, a `length` that returns "hello" (a valid `Object`) triggers no static or runtime warnings or errors.
299
300 Strong mode enforces the stricter, [traditional function subtyping](https://en.w ikipedia.org/wiki/Subtyping#Function_types) rule: subtyping is contravariant in parameter types and covariant in return types. This permits:
301
302 `() -> int` <:<sub>S</sub> `() -> Object`
303
304 but disallows:
305
306 `() -> Object` <:<sub>S</sub> `() -> int`
307
308 With respect to our example, strong mode requires that any subtype of a List hav e an int-typed length. It statically rejects the length declaration in MyList.
309
310 ## Generic Methods
311
312 Strong mode introduces generic methods to allow stricter typing on polymorphic m ethods. Such code in standard Dart today often loses static type information. For example, the `Iterable.map` method is declared as below:
Bob Nystrom 2016/02/02 23:02:17 "stricter" -> "more expressive"
vsm 2016/02/03 00:50:05 Done.
313
314 ```dart
315 abstract class Iterable<E> {
316 ...
317 Iterable map(f(E e));
318 }
319 ```
320
321 Regardless of the static type of the function `f`, the `map` always returns an ` Iterable<dynamic>` in standard Dart. As result, standard Dart tools miss the ob vious error on the following code:
322
323 ```dart
324 Iterable<int> results = <int>[1, 2, 3].map((x) => x.toString()); // Static error only in strong mode
325 ```
326
327 The variable `results` is statically typed as if it contains `int` values, altho ugh it clearly contains `String` values at runtime.
328
329 The [generic methods proposal](https://github.com/leafpetersen/dep-generic-metho ds/blob/master/proposal.md) proposes to add proper generic methods to the Dart l anguage as a first class language construct and to make methods such as the `Ite rable.map` generic.
Bob Nystrom 2016/02/02 23:02:17 "proposal proposes to add" -> "proposal adds".
vsm 2016/02/03 00:50:05 Done.
330
331 To enable experimentation, strong mode provides a [generic methods prototype](GE NERIC_METHODS.md) based on the existing proposal, but usable on all existing Dar t implementations today. Strong mode relies on this to report the error on the example above.
332
333 The `Iterable.map` method is now declared as follows:
334
335 ```dart
336 abstract class Iterable<E> {
337 ...
338 Iterable/*<T>*/ map/*<T>*/(/*=T*/ f(E e));
339 }
340 ```
341
342 At a use site, the generic type may be explicitly provided or inferred from cont ext:
343
344 ```
345 var l = <int>[1, 2, 3];
346 var i1 = l.map((i) => i + 1);
347 var l2 = l.map/*<String>*/((i) { ... });
348 ```
349
350 In the first invocation of `map`, the closure is inferred (from context) as `int -> int`, and the generic type of map is inferred as `int` accordingly. As a re sult, `i1` is inferred as `Iterable<int>`. In the second, the type parameter is explicitly bound to `String`, and the closure is checked against this type. `i 2` is typed as `Iterable<String>`.
351
352 Further details on generic methods in strong mode and in DDC may be found [here] (GENERIC_METHODS.md).
353
354 ## Additional Restrictions
355
356 In addition to stricter typing rules, strong mode enforces other
357 restrictions on Dart programs.
358
359 ### Warnings as Errors
360
361 Strong mode effectively treats all standard Dart static warnings as static error s. Most of these warnings are required for soundness (e.g., if a concrete class is missing methods required by a declared interface). A full list of Dart stat ic warnings may found in the [Dart specification](http://www.google.com/url?q=ht tp%3A%2F%2Fwww.ecma-international.org%2Fpublications%2Ffiles%2FECMA-ST%2FECMA-40 8.pdf&sa=D&sntz=1&usg=AFQjCNGoFPzBNx2fgejKQgSgiS2dUBstBw), or enumerated here:
Jennifer Messerly 2016/02/02 23:07:45 BTW: a feature of Markdown, is you can do referenc
vsm 2016/02/03 00:50:05 Done.
362
363 [https://github.com/dart-lang/sdk/blob/master/pkg/analyzer/lib/src/generated/err or.dart#L3772](https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fdart-lang %2Fsdk%2Fblob%2Fmaster%2Fpkg%2Fanalyzer%2Flib%2Fsrc%2Fgenerated%2Ferror.dart%23L 3772&sa=D&sntz=1&usg=AFQjCNFc4E37M1PshVcw4zk7C9jXgqfGbw)
364
365 ### Super Invocations
366
367 In the context of constructor initializer lists, strong mode restricts `super` i nvocations to the end. This restriction simplifies generated code with minimal effect on the program.
368
369 ### For-in loops
370
371 In for-in statements of the form:
372
373 ```dart
374 for (var i in e) { … }
375 ```
376
377 Strong mode requires the expression `e` to be an `Iterable`. When the loop vari able `i` is also statically typed:
378
379 ```dart
380 for (T i in e) { … }
381 ```
382
383 the expression `e` is required to be an `Iterable<T>`.
384
385 *Note: we may weaken these.*
386
387 ### Field overrides
388
389 By default, fields are overridable in Dart.
390
391 ```dart
392 int init(int n) {
393 print('Initializing with $n');
394 return n;
395 }
396
397 class A {
398 int x = init(42);
399 }
400
401 class B extends A {
402 int x;
403 }
404 ```
405
406 Disallow overriding fields: this results in complicated generated
407 code where a field definition in a subclass shadows the field
408 definition in a base class but both are generally required to be
409 allocated. Users should prefer explicit getters and setters in such
410 cases. See [issue 52](https://github.com/dart-lang/dev_compiler/issues/52).
411
412 ### Open Items
413
414 - Is / As restrictions: Dart's `is` and `as` checks are unsound for certain type s
415 (generic classes, certain function types). In [DDC](RUNTIME_SAFETY.md), problem atic
416 `is` and `as` checks trigger runtime errors. We are considering introducing sta tic
417 errors for these cases.
OLDNEW
« doc/RUNTIME_SAFETY.md ('K') | « doc/RUNTIME_SAFETY.md ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698