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

Side by Side Diff: STATIC_SAFETY.md

Issue 1541833002: Update strong mode doc (Closed) Base URL: https://github.com/dart-lang/dev_compiler.git@master
Patch Set: More runtime headings Created 4 years, 11 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
« RUNTIME_SAFETY.md ('K') | « 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 for Dart
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.
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 types based upon context. In the example above, strong mode infers that `list` has type `List`.
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.
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.
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. It also may alone be used alone for stricter error checking.
Leaf 2016/01/15 00:01:10 'may alone be used alone' -> extra alone I think
vsm 2016/01/15 20:13:39 Done.
31
32 ## Usage
33
34 Strong mode is now integrated into the Dart Analyzer. The analyzer may be invok ed in strong mode as follows:
35
36 $ dartanalyzer --strong myapp.dart
37
38 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:
39
40 ```
41 analyzer:
42 strong-mode: true
43 ```
44
45 ## Type Inference
46
47 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 .
48
49 In Dart, per the specification, the static type of a variable `x` declared as:
50
51 ```dart
52 var x = <String, String>{ "hello": "world"};
53 ```
54
55 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.
56
57 To avoid this, strong mode uses type inference. In the case above, the strong m ode infers and enforces the type of `x` as `Map<String, String>`. An important aspect to inference is ordering: when an inferred type may be used to infer othe r type. To maximize the impact, we perform the following inference in the follo wing order:
Leaf 2016/01/15 00:01:10 'the strong mode' -> 'strong mode' ? 'other type'
vsm 2016/01/15 20:13:39 Done.
58
59 TODO(vsm): Order is out-of-date:
60
61 - Top-level and static fields
62 - Instance fields and methods
63 - Local variables
64 - Allocation expressions
65 - Generic method invocations
66
67 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.
68
69 In [DDC](TODO), inference may also affect the reified runtime type.
70
71
72 In all cases, inference tightens the static type or runtime type as compared to the Dart specification. The `dynamic` type, either alone or in the context of a function or generic parameter type, is inferred to a more specific type. The e ffect of this inference (other than stricter type errors) should not be observab le at runtime outside the use of the mirrors API or by explicitly examining Obje ct.runtimeType. (Note, in the next section, we discuss corresponding restrictio ns on `is` and `as` type checks.)
73
74 ### Top-level and Static Fields
75
76 Strong mode will infer the static type of any top-level or static field with:
77
78 - No static type annotation
79 - An initializer expression
80
81 The static type of the declared variable is inferred as the static type of the i nitializer. For example, consider:
82
83 ```dart
84 var PI = 3.14159;
85 var TAU = PI * 2;
86 ```
87
88 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:
89 ```dart
90 PI = "\u{03C0}"; // Unicode string for PI symbol
91 ```
92 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,
93
94 ```dart
95 var _PI_FIRST = true;
96 var PI = _PI_FIRST ? 3.14159 : TAU / 2;
97 var TAU = _PI_FIRST ? PI * 2 : 6.28318;
98 ```
99
100 the variables `PI` and `TAU` are circularly dependent on each other. Strong mod e would leave the static type of both as `dynamic`.
101
102 ### Instance Fields and Methods
103
104 Strong mode performs two types of inference on instances fields and methods.
105
106 The first uses base types to constrain overrides in subtypes. Consider the foll owing example:
107
108 ```dart
109 abstract class A {
110 Map get m;
111 int value(int i);
112 }
113
114 class B extends A {
115 var m;
116 value(i) => m[i];
117
118 }
119 ```
120
121 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`.
122
123 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`.
124
125 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:
126
127 ```dart
128 class B extends A {
129 Map m;
130 int value(int i) => m[i];
131
132 }
133 ```
134
135 Note that tightening the argument type for `i` is not required for soundness; it is done for convenience as it is the typical intent. The programmer may explic itly type this as `dynamic` or `Object` to avoid inferring the narrower type.
136
137 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:
138
139 ```dart
140 class C extends A {
141 var y = 42;
142 var m = <int, int>{ 0: 38};
143 ...
144 }
145 ```
146
147 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`.
148
149 ### Local Variables
150
151 As with fields, local variable types are inferred if the static type is omitted and an initializer expression is present. In the following example:
152
153 ```dart
154 Object foo(int x) {
155 final y = x + 1;
156 var z = y * 2;
157 return z;
158 }
159 ```
160
161 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:
162 ```dart
163 z = “$z”;
164 ```
165 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 avoid infe rence. Explicitly declaring `z` with the type `Object` or `dynamic` would suffi ce in this case.
166
167 ### Allocation Expressions
168
169 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`).
170
171 #### Closure literals
172
173 Consider the following example:
174
175 ```dart
176 int apply(int f(int arg), int value) {
177 return f(value);
178 }
179
180 void main() {
181 int result =
182 apply((x) { x = x * 9 ~/ 5; return x + 32; }, 41);
183 print(result);
184 }
185 ```
186
187 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.
188
189 Dart has a syntactic limitation in this case: it is not possible to statically a nnotate the return type of a closure literal.
190
191 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.
192
193 #### List and map literals
194
195 Similarly, strong mode infers tighter runtime types for list and map literals. E.g., in
196
197 ```dart
198 List<String> l = [ "hello", "world" ];
199 ```
200
201 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:
202
203 ```dart
204 List<String> l = <String>[ "hello", "world" ];
205 ```
206
207 Similarly, the following will now trigger a static error in strong mode:
208
209 ```dart
210 List<String> l = [ "hello", 42 ]; // Strong Mode Error: 42 is not a String
211 ```
212
213 Contextual inference may be recursive:
214
215 ```dart
216 Map<List<String>, Map<int, int>> map =
217 { ["hello"]: { 0: 42 }};
218 ```
219
220 In this case, the inner map literal is inferred and allocated as a `Map<int, int >`. Note, strong mode will statically reject code where the contextually requir ed type is not compatible. This will trigger a static error:
221
222 ```dart
223 Map<List<String>, Map<int, int>> map =
224 { ["hello"]: { 0: "world" }}; // STATIC ERROR
225 ```
226
227 as "world" is not of type `int`.
228
229 #### Constructor invocations
230
231 Finally, strong mode performs similar contextual inference on explicit construct or invocations via new or const. For example:
232
233 ```dart
234 Set<String> string = new Set.from(["hello", "world"]);
235 ```
236
237 is treated as if it was written as:
238
239 ```dart
240 Set<String> string =
241 new Set<String>.from(<String>["hello", "world"]);
242 ```
243
244 Note, as above, context is propagated downward into the expression.
245
246 ### Generic method inference
247
248 TODO(vsm)
249
250 ## Strict subtyping
251 The primary sources of unsoundness in Dart are generics and functions. Both int roduce circularity in the Dart subtyping relationship.
252
253 ### Generics
254
255 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`:
256
257 `List<S>` <:<sub>D</sub> `List<dynamic>` <:<sub>D</sub> `List<T>`
258
259 where `List` is equivalent to `List<dynamic>`. This introduces circularity - e. g.:
260
261 `List<int>` <:<sub>D</sub> `List` <:<sub>D</sub> `List<String>`<:<sub>D</sub> `L ist` <:<sub>D</sub> `List<int>`
262
263
264 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.
265
266 Our running example exploits this. A `MyList` may be passed to the `info` funct ion as it’s a subtype of the expected type:
267
268 `MyList` <:<sub>D</sub> `List` <:<sub>D</sub>`List<int>`
269
270 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:
271
272 `List<int>` <:<sub>S</sub> `List`
273
274 but that this is not:
275
276 `List<int>` ~~<:<sub>S</sub> `List`~~
277
278
279 Our running example fails in strong mode:
280
281 `MyList` <:<sub>S</sub> `List` ~~<:<sub>S</sub> `List<int>`~~
282
283
284 ### Functions
285
286 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:
287
288 `(int) -> int` <:<sub>D</sub> `(Object) -> Object` <:<sub>D</sub> `(int) -> int`
289
290 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:
291
292 `() -> Object`
293
294 while the `List.length` getter it overrides has type:
295
296 `() -> int`
297
298 This is valid in standard Dart as:
299
300 `() -> Object` <:<sub>D</sub> `() -> int`
301
302 Because of this, a `length` that returns "hello" (a valid `Object`) triggers no static or runtime warnings or errors.
303
304 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:
305
306 `() -> int` <:<sub>S</sub> `() -> Object`
307
308 but disallows:
309
310 `() -> Object` <:<sub>S</sub> `() -> int`
311
312 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.
313
314 Formal details of the strong mode type system may be found [here](https://dart- lang.github.io/dev_compiler/strong-dart.pdf).
315
316 ## Generic Methods
317
318 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:
319
320 ```dart
321 abstract class Iterable<E> {
322 ...
323 Iterable map(f(E e));
324 }
325 ```
326
327 Regardless of the static type of the function `f`, the `map` always returns an ` Iterable<dynamic>`.
328
329 TODO(vsm): Link to DEP.
330
331 Generic methods have been proposed as an addition to the Dart language. To enab le experimentation, strong mode provides a comment-based form of generic methods based on the existing DEP, but usable on all existing Dart implementations. Th e `Iterable.map` method is declared as follows:
332
333 ```dart
334 abstract class Iterable<E> {
335 ...
336 Iterable/*<T>*/ map/*<T>*/(/*=T*/ f(E e));
337 }
338 ```
339
340 At a use site, the generic type may be explicitly provided or inferred from cont ext:
341
342 ```
343 var l = <int>[1, 2, 3];
344 var i1 = l.map((i) => i + 1);
345 var l2 = l.map/*<String>*/((i) { ... });
346 ```
347
348 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>`.
349
350
351 ## Additional Restrictions
352
353 In addition to stricter typing rules, strong mode enforces other
354 restrictions on Dart programs.
355
356 ### Warnings as Errors
357
358 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:
359
360 [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)
361
362 ### Is / As Restrictions
363
364 TODO(vsm): Fix this
365
366 Dart is and as runtime checks expose the unsoundness of the type system in certa in cases. For example, consider:
367
368 ```dart
369 var list = ["hello", "world"];
370 if (list is List<int>) {
371 ...
372 } else if (list is List<String>) {
373 ...
374 }
375 ```
376
377 Perhaps surprisingly, the first test - `list is List<int>` - evaluates to true h ere. Such code is highly likely to be erroneous.
378
379 Strong mode statically disallows problematic `is` or `as` checks.. In general, an expression:
380
381 ```dart
382 x is T
383 ```
384
385 or
386
387 ```dart
388 x as T
389 ```
390
391 is only allowed where `T` is a *ground type*:
392
393 - A non-generic class type (e.g., `Object`, `String`, `int`, ...).
394 - A generic class type where all type parameters are implicitly or explicitly `d ynamic` (e.g., `List<dynamic>`, `Map`, …).
395 - A function type where the return type and all parameter types are `dynamic` (e .g., (`dynamic`, `dynamic`) -> `dynamic`, ([`dynamic`]) -> `dynamic`).
396
397 In all other cases, strong mode reports a static error.
398
399 ### Super Invocations
400
401 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.
402
403 ### For-in loops
404
405 In for-in statements of the form:
406
407 ```dart
408 for (var i in e) { … }
409 ```
410
411 Strong mode requires the expression `e` to be an `Iterable`. When the loop var iable `i` is also statically typed:
412
413 ```dart
414 for (T i in e) { … }
415 ```
416
417 the expression `e` is required to be an `Iterable<T>`.
418
419 *Note: we may weaken these.*
420
421 ### Await Expressions
422
423 In an await expression of the form:
424 ```dart
425 await expr
426 ```
427 strong mode requires `expr` to be a subtype of `Future`. In standard Dart, this is not required although tools may provide a hint.
428
429 ### Open Items
430
431 TODO(vsm): Field overrides are now restricted.
432
433 We do not yet implement but are considering the following restrictions as well.
434
435 - Disallow overriding fields: this results in complicated generated
436 code where a field definition in a subclass shadows the field
437 definition in a base class but both are generally required to be
438 allocated. Users should prefer explicit getters and setters in such
439 cases. See [issue 52](https://github.com/dart-lang/dev_compiler/issues/52).
440
441 - `Future<Future<T>>`: the Dart specification automatically flattens
442 this type to `Future<T>` (where `T` is not a `Future`). This can be
443 issue as far as soundness. We are considering forbidding this type
444 altogether (with a combination of static and runtime checks). See
445 [issue 228](https://github.com/dart-lang/dev_compiler/issues/228).
446
447 ## DDC, Runtime checks, and Soundness
448
449 TODO(vsm): Move this to a different doc?
450
451 ### Runtime Checks
452
453 ### Type Narrowing
OLDNEW
« RUNTIME_SAFETY.md ('K') | « RUNTIME_SAFETY.md ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698