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

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: Address Bob's comments 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
« no previous file with comments | « no previous file | 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
1 # Strong Mode 1 # Strong Mode for Dart
2 2
3 ## Overview 3 ## Overview
4 4
5 The Dart Development Compiler (DDC) is a new Dart-to-JavaScript compiler that su pports a large subset of the Dart programming language. DDC is motivated by the following goals: 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 6
7 First, we aim to generate clean, readable, consumable JavaScript output. This s implifies debugging Dart applications across multiple web platforms. It also en ables better, seamless interoperability between Dart and JavaScript components. 7 In particular, static type annotations are often misleading. Dart code such as:
8
9 Second, we aim to compile in a fast, modular fashion. This enables a faster, be tter development cycle across a number of platforms and devices that lack native Dart support. It also allows Dart libraries to be packaged and distributed sep arately for use in other Dart or JavaScript applications.
10
11 To accomplish these goals, we focus on a subset of Dart applications we can stat ically type check. This subset can be viewed as a **strong mode** analogous to Dart’s checked mode and production mode. A program that runs correctly in stron g mode will run the same in checked mode and, thus, in production mode. The sub set we support entails:
12
13 - A stricter, sounder, type system
14 - Type inference
15 - Restrictions on certain language constructs
16
17 DDC is intended to complement our existing Dart2JS compiler. Unlike DDC, Dart2J S is focused on raw performance and support for the entire Dart language rather than readability, JavaScript interoperability, or modular compilation.
18
19 This document provides a high-level overview of strong mode. A corresponding fo rmalism of strong mode can also be found [here](https://dart-lang.github.io/dev_ compiler/strong-dart.pdf).
20
21 ## Motivation
22
23 The standard Dart type system is unsound by design. This means that static type annotations may not match the actual runtime values even when a program is runn ing in checked mode. This allows considerable flexibility, but it also means th at Dart implementations cannot easily use these annotations for optimization or code generation.
24
25 Because of this, existing Dart implementations require dynamic dispatch. Furthe rmore, because Dart’s dispatch semantics are different from JavaScript’s, it eff ectively precludes mapping Dart calls to idiomatic JavaScript. For example, the following Dart code:
26 8
27 ```dart 9 ```dart
28 var x = a.bar; 10 var list = ["hello", "world"];
29 b.foo("hello", x); 11 List<int> listOfInts = list;
30 ``` 12 ```
31 13
32 cannot easily be mapped to the identical JavaScript code. If `a` does not conta in a `bar` field, Dart requires a `NoSuchMethodError` while JavaScript simply re turns undefined. If `b` contains a `foo` method, but with the wrong number of a rguments, Dart again requires a `NoSuchMethodError` while JavaScript either igno res extra arguments or fills in omitted ones with undefined. 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`:
33
34 To capture these differences, the Dart2JS compiler instead generates code that a pproximately looks like:
35 15
36 ```dart 16 ```dart
37 var x = getInterceptor(a).get$bar(a); 17 Iterable<int> iterableOfInts = listOfInts.map((i) => i.abs());
38 getInterceptor(b).foo$2(b, "hello", x);
39 ```
40 The “interceptor” is Dart’s dispatch table for the objects `a` and `b`, and the mangled names (`get$bar` and `foo$2`) account for Dart’s different dispatch sema ntics.
41
42 The above highlights why Dart-JavaScript interoperability hasn’t been seamless: Dart objects and methods do not look like normal JavaScript ones.
43
44 DDC relies on strong mode to map Dart calling conventions to normal JavaScript o nes. If `a` and `b` have static type annotations (with a type other than `dynam ic`), strong mode statically verifies that they have a field `bar` and a 2-argum ent method `foo` respectively. In this case, DDC safely generates the identical JavaScript:
45
46 ```javascript
47 var x = a.bar;
48 b.foo("hello", x);
49 ``` 18 ```
50 19
51 Note that DDC still supports the `dynamic` type, but relies on runtime helper fu nctions in this case. E.g., if `a` and `b` are type `dynamic`, DDC instead gene rates: 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.
52 21
53 ```javascript 22 To accomplish this, strong mode involves the following:
54 var x = dload(a, "bar"); 23
55 dsend(b, "foo", "hello", x); 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.
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
56 ``` 43 ```
57 44
58 where `dload` and `dsend` are runtime helpers that implement Dart dispatch seman tics. Programmers are encouraged to use static annotations to avoid this. Stron g mode is able to use static checking to enforce much of what checked mode does at runtime. In the code above, strong mode statically verifies that `b`’s type (if not `dynamic`) has a `foo` method that accepts a `String` as its first argum ent and `a.bar`’s type as its second. If the code is sufficiently typed, runtim e checks are unnecessary. 45 ## Type Inference
59 46
60 ## Strong Mode Type System 47 An explicit goal of strong mode to provide stronger typing while preserving the terseness of Dart. Dart syntax and [common style] encourages a modest level of type annotations. Strong mode should not compel programmers to add more. Inste ad, it relies on type inference.
61
62 DDC uses strong mode to ensure that static type annotations are actually correct at runtime. For this to work, strong mode requires a stricter type system than standard Dart. To understand this, consider the following, which we will use a s our running example:
63
64 ```dart
65 library util;
66
67 void info(List<int> list) {
68 var length = list.length;
69 if (length != 0) print("$length ${list[0]}");
70 }
71 ```
72
73 A developer might reasonably expect the `info` function to print either nothing (empty list) or two integers (non-empty list), and that Dart’s static tooling an d checked mode would enforce this.
74
75 However, in the following context, the info method prints “hello world” in check ed mode, without any static errors or warnings:
76
77 ```dart
78 import ‘dart:collection’;
79 import ‘utils.dart’;
80
81 class MyList extends ListBase<int> implements List {
82 Object length;
83
84 MyList(this.length);
85
86 operator[](index) => "world";
87 operator[]=(index, value) {}
88 }
89
90 void main() {
91 List<int> list = new MyList("hello");
92 info(list);
93 }
94 ```
95
96 The lack of static or runtime errors is not an oversight; it is by design. It p rovides developers a mechanism to circumvent or ignore types when convenient, bu t it comes at cost. While the above example is contrived, it demonstrates that developers cannot easily reason about a program modularly: the static type annot ations in the `utils` library are of limited use, even in checked mode.
97
98 For the same reason, a compiler cannot easily exploit type annotations if they a re unsound. A Dart compiler cannot simply assume that a `List<int>` contains `i nt` values or even that its `length` is an integer. Instead, it must either rel y on expensive (and often brittle) whole program analysis or on additional runti me checking.
99
100 The fundamental issue above is that static annotations may not match runtime typ es, even in checked mode: this is a direct consequence of the unsoundness of the Dart type system. This can make it difficult for both programmers and compiler s to rely on static types to reason about programs.
101
102 Strong mode enforces the correctness of static type annotations. It simply disa llows examples such as the above. It relies on a combination of static checking and runtime assertions. In our running example, standard Dart rules (checked or otherwise) allow `MyList` to masquerade as a `List<int>`. DDC disallows this b y statically rejecting the declaration of `MyList`. This allows both the develo per and the compiler to better reason about the info method. For statically che cked code, both may assume that the argument is a proper `List<int>`, with integ er-valued length and elements.
103
104 DDC’s strong mode is strictly stronger than checked mode. A Dart program execut ion where (a) the program passes DDC’s static checking and (b) the execution doe s not trigger DDC’s runtime assertions, will also run in checked mode on any Dar t platform.
105
106
107 ### Static typing
108
109 The primary sources of unsoundness in Dart are generics and functions. Both int roduce circularity in the Dart subtyping relationship.
110
111 #### Generics
112
113 Generics in Dart are co-variant, with the added rule that the `dynamic` type may serve as both ⊤ (top) and ⊥ (bottom) of the type hierarchy in certain situation s. For example, let *<:<sub>D</sub>* represent the standard Dart subtyping rul e. Then, for all types `S` and `T`:
114
115 `List<S>` <:<sub>D</sub> `List<dynamic>` <:<sub>D</sub> `List<T>`
116
117 where `List` is equivalent to `List<dynamic>`. This introduces circularity - e. g.,:
118
119 `List<int>` <:<sub>D</sub> `List` <:<sub>D</sub> `List<String>`<:<sub>D</sub> `L ist` <:<sub>D</sub> `List<int>`
120
121
122 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.
123
124 Our running example exploits this. A `MyList` may be passed to the `info` funct ion as it’s a subtype of the expected type:
125
126 `MyList` <:<sub>D</sub> `List` <:<sub>D</sub>`List<int>`
127
128 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:
129
130 `List<int>` <:<sub>S</sub> `List`
131
132 but that this is not:
133
134 `List<int>` ~~<:<sub>S</sub> `List`~~
135
136
137 Our running example fails in strong mode:
138
139 `MyList` <:<sub>S</sub> `List` ~~<:<sub>S</sub> `List<int>`~~
140
141
142 #### Functions
143
144 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:
145
146 `(int) -> int` <:<sub>D</sub> `(Object) -> Object` <:<sub>D</sub> `(int) -> int`
147
148 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:
149
150 `() -> Object`
151
152 while the `List.length` getter it overrides has type:
153
154 `() -> int`
155
156 This is valid in standard Dart as:
157
158 `() -> Object` <:<sub>D</sub> `() -> int`
159
160 Because of this, a `length` that returns "hello" (a valid `Object`) triggers no static or runtime warnings or errors.
161
162 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:
163
164 `() -> int` <:<sub>S</sub> `() -> Object`
165
166 but disallows:
167
168 `() -> Object` <:<sub>S</sub> `() -> int`
169
170 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.
171
172 Formal details of the strong mode type system may be found [here](https://dart- lang.github.io/dev_compiler/strong-dart.pdf).
173
174 ### Implicit runtime assertions
175
176 Although strong mode relies heavily on static checking, it also requires some ru ntime checking for soundness. For example, the following code is allowed:
177
178 ```dart
179 dynamic x = …;
180 List y = x;
181 int z = y.length;
182 ```
183
184 but a runtime check is required (and inserted by DDC) to ensure that `y` is assi gned a `List`. This is similar to checked mode, but much less pervasive. Check ed mode would also require a runtime check on the assignment of `z`. Strong mod e would not as this is enforced statically instead.
185
186 ## Type Inference
187
188 A secondary goal of DDC is to preserve the terseness of Dart. While strong mode requires and/or encourages more static type annotations, our aim is make this a s lightweight as possible.
189 48
190 In Dart, per the specification, the static type of a variable `x` declared as: 49 In Dart, per the specification, the static type of a variable `x` declared as:
191 50
192 ```dart 51 ```dart
193 var x = <String, String>{ "hello": "world"}; 52 var x = <String, String>{ "hello": "world"};
194 ``` 53 ```
195 54
196 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. 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.
197 56
198 To avoid this, strong mode uses limited inference. In the case above, the stron g mode infers and enforces the type of `x` as `Map<String, String>`. An importa nt aspect to inference is ordering: when an inferred type may be used to infer o ther type. To maximize the impact, we perform the following inference in the fo llowing order: 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:
199 58
200 - Top-level and static fields 59 - Top-level and static fields
201 - Instance fields and methods 60 - Instance fields and methods
202 - Local variables 61 - Local variables
203 - Allocation expressions 62 - Allocation expressions
Leaf 2016/01/14 01:07:17 Generic method argument inference as well. It may
vsm 2016/01/14 16:35:14 The order here is based on the old code in dev_com
Leaf 2016/01/15 00:01:10 Yes, though the generic type inference is not actu
204 63
64 TODO(vsm): Move runtime discussion
65
205 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.) 66 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.)
206 67
207 ### Top-level and Static Fields 68 ### Top-level and Static Fields
208 69
209 Strong mode will infer the static type of any top-level or static field with: 70 Strong mode will infer the static type of any top-level or static field with:
210 71
211 - No static type annotation 72 - No static type annotation
212 - An initializer expression 73 - An initializer expression
213 74
214 The static type of the declared variable is inferred as the static type of the i nitializer. For example, consider: 75 The static type of the declared variable is inferred as the static type of the i nitializer. For example, consider:
215 76
216 ```dart 77 ```dart
217 var PI = 3.14159; 78 var PI = 3.14159;
218 var TAU = PI * 2; 79 var TAU = PI * 2;
219 ``` 80 ```
220 81
221 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: 82 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:
222 ```dart 83 ```dart
223 PI = "\u{03C0}"; // Unicode string for PI symbol 84 PI = "\u{03C0}"; // Unicode string for PI symbol
224 ``` 85 ```
225 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, 86 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,
226 87
227 ```dart 88 ```dart
228 var _PI_FIRST = true; 89 var _PI_FIRST = true;
229 var PI = _PI_FIRST ? 3.14159 : TAU / 2; 90 var PI = _PI_FIRST ? 3.14159 : TAU / 2;
230 var TAU = _PI_FIRST ? PI * 2 : 6.28318; 91 var TAU = _PI_FIRST ? PI * 2 : 6.28318;
231 ``` 92 ```
232 93
233 the variables `PI` and `TAU` are circularly dependent on each other. Strong mod e would leave the static type of both as `dynamic`. 94 the variables `PI` and `TAU` are circularly dependent on each other. Strong mod e would leave the static type of both as `dynamic`.
234
235 <em>
236 Note - we’re experimenting with a few arguably simpler variants here:
237 - Limiting inference to final or const fields (i.e., not var).
238 - Limiting transitive inference to explicit program order.
239 </em>
240 95
241 ### Instance Fields and Methods 96 ### Instance Fields and Methods
242 97
243 Strong mode performs two types of inference on instances fields and methods. 98 Strong mode performs two types of inference on instances fields and methods.
244 99
245 The first uses base types to constrain overrides in subtypes. Consider the foll owing example: 100 The first uses base types to constrain overrides in subtypes. Consider the foll owing example:
246 101
247 ```dart 102 ```dart
248 abstract class A { 103 abstract class A {
249 Map get m; 104 Map get m;
250 int value(int i); 105 int value(int i);
251 } 106 }
252 107
253 class B extends A { 108 class B extends A {
254 var m; 109 var m;
255 value(i) => m[i]; 110 value(i) => m[i];
256 … 111 …
257 } 112 }
258 ``` 113 ```
259 114
260 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`. 115 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`.
261 116
262 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`. 117 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`.
263 118
264 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: 119 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:
265 120
266 ```dart 121 ```dart
267 class B extends A { 122 class B extends A {
268 Map m; 123 Map m;
269 int value(i) => m[i]; 124 int value(int i) => m[i];
270 125
271 } 126 }
272 ``` 127 ```
273 128
274 Note that the argument type of `value` is left as `dynamic`. Tightening this ty pe is not required for soundness. 129 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.
275 130
276 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: 131 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:
Leaf 2016/01/14 01:07:17 missing 'of' between 'form' and 'inference'.
277 132
278 ```dart 133 ```dart
279 class C extends A { 134 class C extends A {
280 var y = 42; 135 var y = 42;
281 var m = <int, int>{ 0: 38}; 136 var m = <int, int>{ 0: 38};
282 ... 137 ...
283 } 138 }
284 ``` 139 ```
140
285 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`. 141 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`.
286 142
287 <em>
288 Note - we’re considering with a few variants here as well:
289 - Limiting inference to final or const fields (i.e., not var).
290 - Inference on parameter types when omitted (e.g., the argument to `B.value` abo ve).
291 - When to allow or prefer override-based inference.
292 </em>
293
294 ### Local Variables 143 ### Local Variables
295 144
296 As with fields, local variable types are inferred if the static type is omitted and an initializer expression is present. In the following example: 145 As with fields, local variable types are inferred if the static type is omitted and an initializer expression is present. In the following example:
297 146
298 ```dart 147 ```dart
299 Object foo(int x) { 148 Object foo(int x) {
300 final y = x + 1; 149 final y = x + 1;
301 var z = y * 2; 150 var z = y * 2;
302 return z; 151 return z;
303 } 152 }
304 ``` 153 ```
305 154
306 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: 155 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:
307 ```dart 156 ```dart
308 z = “$z”; 157 z = “$z”;
309 ``` 158 ```
310 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. 159 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.
311 160
312 ### Allocation Expressions 161 ### Allocation Expressions
313 162
314 The final form of strong mode inference is on allocation expressions. This infe rence is rather different from the above: it tightens the runtime type of the co rresponding expression using the static type of its context. Contextual inferen ce is used on expressions that allocated a new object: closure literals, map and list literals, and explicit constructor invocations (i.e., via `new` or `const` ). 163 The final form of strong mode inference is on allocation expressions. This infe rence is rather different from the above: it tightens the runtime type of the co rresponding expression using the static type of its context. Contextual inferen ce is used on expressions that allocated a new object: closure literals, map and list literals, and explicit constructor invocations (i.e., via `new` or `const` ).
Leaf 2016/01/14 01:07:17 I think "expressions that allocated" reads better
Leaf 2016/01/14 01:07:17 Final form, except generic method arguments.
vsm 2016/01/14 16:35:14 Added section, fixed text.
vsm 2016/01/14 16:35:14 Done.
315 164
316 #### Closure literals 165 #### Closure literals
317 166
318 Consider the following example: 167 Consider the following example:
319 168
320 ```dart 169 ```dart
321 int apply(int f(int arg), int value) { 170 int apply(int f(int arg), int value) {
322 return f(value); 171 return f(value);
323 } 172 }
324 173
325 void main() { 174 void main() {
326 int result = 175 int result =
327 apply((x) { x = x * 9 ~/ 5; return x + 32; }, 41); 176 apply((x) { x = x * 9 ~/ 5; return x + 32; }, 41);
328 print(result); 177 print(result);
329 } 178 }
330 ``` 179 ```
331 180
332 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. 181 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.
333 182
334 Dart has a syntactic limitation in this case: it is not possible to statically a nnotate the return type of a closure literal. 183 Dart has a syntactic limitation in this case: it is not possible to statically a nnotate the return type of a closure literal.
335 184
336 Strong mode sidesteps this difficulty via contextual inference. It infers the c losure type as `(dynamic) -> int`. This is the most general type allowed by the context: the parameter type of apply. 185 Strong mode sidesteps this difficulty via contextual inference. It infers the c losure type as `(dynamic) -> int`. This is the most general type allowed by the context: the parameter type of apply.
Leaf 2016/01/14 01:07:17 Actually, it will now infer it as `(int) -> int`.
vsm 2016/01/14 16:35:14 Done.
337 186
338 #### List and map literals 187 #### List and map literals
339 188
340 Similarly, strong mode infers tighter runtime types for list and map literals. E.g., in 189 Similarly, strong mode infers tighter runtime types for list and map literals. E.g., in
341 190
342 ```dart 191 ```dart
343 List<String> l = [ "hello", "world" ]; 192 List<String> l = [ "hello", "world" ];
344 ``` 193 ```
345 194
346 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 executes as if it was: 195 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 executes as if it was:
Leaf 2016/01/14 01:07:17 maybe say "the code above is type checked and ex
vsm 2016/01/14 16:35:14 Done.
347 196
348 ```dart 197 ```dart
349 List<String> l = <String>[ "hello", "world" ]; 198 List<String> l = <String>[ "hello", "world" ];
350 ``` 199 ```
351 200
352 Contextual inference may be recursive: 201 Contextual inference may be recursive:
353 202
354 ```dart 203 ```dart
355 Map<List<String>, Map<int, int>> map = 204 Map<List<String>, Map<int, int>> map =
356 { ["hello"]: { 0: 42 }}; 205 { ["hello"]: { 0: 42 }};
357 ``` 206 ```
358 207
359 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: 208 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:
360 209
361 ```dart 210 ```dart
362 Map<List<String>, Map<int, int>> map = 211 Map<List<String>, Map<int, int>> map =
363 { ["hello"]: { 0: "world" }}; // STATIC ERROR 212 { ["hello"]: { 0: "world" }}; // STATIC ERROR
364 ``` 213 ```
365 214
366 as "world" is not of type `int`. 215 as "world" is not of type `int`.
367 216
368 #### Constructor invocations 217 #### Constructor invocations
369 218
370 Finally, strong mode performs similar contextual inference on explicit construct or invocations via new or const. For example: 219 Finally, strong mode performs similar contextual inference on explicit construct or invocations via new or const. For example:
371 220
372 ```dart 221 ```dart
373 Set<String> string = new Set.from(["hello", "world"]); 222 Set<String> string = new Set.from(["hello", "world"]);
374 ``` 223 ```
375 224
376 is treated as if it was written as: 225 is treated as if it was written as:
377 226
378 ```dart 227 ```dart
379 Set<String> string = 228 Set<String> string =
380 new Set<String>.from(<String>["hello", "world"]); 229 new Set<String>.from(<String>["hello", "world"]);
381 ``` 230 ```
382 231
383 Note, as above, context is propagated downward into the expression. 232 Note, as above, context is propagated downward into the expression.
384 233
385 ## General Language Restrictions 234 ## Strict subtyping
235 The primary sources of unsoundness in Dart are generics and functions. Both int roduce circularity in the Dart subtyping relationship.
386 236
387 In addition to stricter typing rules, DDC enforces other restrictions on Dart pr ograms. 237 ### Generics
388 238
389 ### Warnings are Errors 239 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`:
390 240
391 DDC effectively treats all standard Dart static warnings as static errors. Most of these warnings are required for soundness (e.g., if a concrete class is miss ing methods required by a declared interface). A full list of Dart static warni ngs may found in the [Dart specification](http://www.google.com/url?q=http%3A%2F %2Fwww.ecma-international.org%2Fpublications%2Ffiles%2FECMA-ST%2FECMA-408.pdf&sa =D&sntz=1&usg=AFQjCNGoFPzBNx2fgejKQgSgiS2dUBstBw), or enumerated here: 241 `List<S>` <:<sub>D</sub> `List<dynamic>` <:<sub>D</sub> `List<T>`
242
243 where `List` is equivalent to `List<dynamic>`. This introduces circularity - e. g.,:
Leaf 2016/01/14 01:07:17 extra comma "e.g.,:"
vsm 2016/01/14 16:35:14 Done.
244
245 `List<int>` <:<sub>D</sub> `List` <:<sub>D</sub> `List<String>`<:<sub>D</sub> `L ist` <:<sub>D</sub> `List<int>`
246
247
248 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.
249
250 Our running example exploits this. A `MyList` may be passed to the `info` funct ion as it’s a subtype of the expected type:
251
252 `MyList` <:<sub>D</sub> `List` <:<sub>D</sub>`List<int>`
253
254 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:
255
256 `List<int>` <:<sub>S</sub> `List`
257
258 but that this is not:
259
260 `List<int>` ~~<:<sub>S</sub> `List`~~
261
262
263 Our running example fails in strong mode:
264
265 `MyList` <:<sub>S</sub> `List` ~~<:<sub>S</sub> `List<int>`~~
266
267
268 ### Functions
269
270 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:
271
272 `(int) -> int` <:<sub>D</sub> `(Object) -> Object` <:<sub>D</sub> `(int) -> int`
273
274 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:
275
276 `() -> Object`
277
278 while the `List.length` getter it overrides has type:
279
280 `() -> int`
281
282 This is valid in standard Dart as:
283
284 `() -> Object` <:<sub>D</sub> `() -> int`
285
286 Because of this, a `length` that returns "hello" (a valid `Object`) triggers no static or runtime warnings or errors.
287
288 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:
289
290 `() -> int` <:<sub>S</sub> `() -> Object`
291
292 but disallows:
293
294 `() -> Object` <:<sub>S</sub> `() -> int`
295
296 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.
297
298 Formal details of the strong mode type system may be found [here](https://dart- lang.github.io/dev_compiler/strong-dart.pdf).
299
300 ## Generic Methods
301
302 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:
303
304 ```dart
305 abstract class Iterable<E> {
306 ...
307 Iterable map(f(E e));
308 }
309 ```
310
311 Regardless of the static type of the function `f`, the `map` always returns an ` Iterable<dynamic>`.
312
313 TODO(vsm): Link to DEP.
314
315 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:
316
317 ```dart
318 abstract class Iterable<E> {
319 ...
320 Iterable/*<T>*/ map/*<T>*/(/*=T*/ f(E e));
321 }
322 ```
323
324 At a use site, the generic type may be explicitly provided or inferred from cont ext:
325
326 ```
327 var l = <int>[1, 2, 3];
328 var i1 = l.map((i) => i + 1);
329 var l2 = l.map/*<String>*/((i) { ... });
330 ```
331
332 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>`.
333
334
335 ## Additional Restrictions
336
337 In addition to stricter typing rules, strong mode enforces other
338 restrictions on Dart programs.
339
340 ### Warnings as Errors
341
342 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:
392 343
393 [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) 344 [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)
394 345
395 ### Is / As Restrictions 346 ### Is / As Restrictions
347
348 TODO(vsm): Fix this
396 349
397 Dart is and as runtime checks expose the unsoundness of the type system in certa in cases. For example, consider: 350 Dart is and as runtime checks expose the unsoundness of the type system in certa in cases. For example, consider:
398 351
399 ```dart 352 ```dart
400 var list = ["hello", "world"]; 353 var list = ["hello", "world"];
401 if (list is List<int>) { 354 if (list is List<int>) {
402 ... 355 ...
403 } else if (list is List<String>) { 356 } else if (list is List<String>) {
404 ... 357 ...
405 } 358 }
406 ``` 359 ```
407 360
408 Perhaps surprisingly, the first test - `list is List<int>` - evaluates to true h ere. Such code is highly likely to be erroneous. 361 Perhaps surprisingly, the first test - `list is List<int>` - evaluates to true h ere. Such code is highly likely to be erroneous.
409 362
410 DDC strong mode statically disallows problematic `is` or `as` checks.. In gener al, an expression: 363 Strong mode statically disallows problematic `is` or `as` checks.. In general, an expression:
411 364
412 ```dart 365 ```dart
413 x is T 366 x is T
414 ``` 367 ```
415 368
416 or 369 or
417 370
418 ```dart 371 ```dart
419 x as T 372 x as T
420 ``` 373 ```
421 374
422 is only allowed where `T` is a *ground type*: 375 is only allowed where `T` is a *ground type*:
423 376
424 - A non-generic class type (e.g., `Object`, `String`, `int`, ...). 377 - A non-generic class type (e.g., `Object`, `String`, `int`, ...).
Leaf 2016/01/14 01:07:17 extra comma after e.g.?
vsm 2016/01/14 16:35:14 Going British on us? :-) https://en.wiktionary.o
Leaf 2016/01/15 00:01:10 Heh... too much Wodehouse I guess.
425 - A generic class type where all type parameters are implicitly or explicitly `d ynamic` (e.g., `List<dynamic>`, `Map`, …). 378 - A generic class type where all type parameters are implicitly or explicitly `d ynamic` (e.g., `List<dynamic>`, `Map`, …).
Leaf 2016/01/14 01:07:17 ditto?
426 - A function type where the return type and all parameter types are `dynamic` (e .g., (`dynamic`, `dynamic`) -> `dynamic`, ([`dynamic`]) -> `dynamic`). 379 - A function type where the return type and all parameter types are `dynamic` (e .g., (`dynamic`, `dynamic`) -> `dynamic`, ([`dynamic`]) -> `dynamic`).
Leaf 2016/01/14 01:07:17 ditto?
427 380
428 In all other cases, strong mode reports a static error. 381 In all other cases, strong mode reports a static error.
429 382
430 ### Super Invocations 383 ### Super Invocations
431 384
432 In the context of constructor initializer lists, DDC restricts `super` invocatio ns to the end. This restriction simplifies generated code with minimal effect o n the program. 385 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.
433 386
434 *Note: Both the VM and Dart2JS ignore the Dart specification on ordering: i.e., they appear to invoke the super constructor after other items on the initializer list regardless of where it appears.* 387 ### For-in loops
435 388
436 ### For-in Loops 389 In for-in statements of the form:
437
438 In for-in statements of the form:
439 390
440 ```dart 391 ```dart
441 for (var i in e) { … } 392 for (var i in e) { … }
442 ``` 393 ```
443 394
444 Strong mode requires the expression `e` to be an `Iterable`. When the loop var iable `i` is also statically typed: 395 Strong mode requires the expression `e` to be an `Iterable`. When the loop var iable `i` is also statically typed:
445 396
446 ```dart 397 ```dart
447 for (T i in e) { … } 398 for (T i in e) { … }
448 ``` 399 ```
449 400
450 the expression `e` is required to be an `Iterable<T>`. 401 the expression `e` is required to be an `Iterable<T>`.
451 402
452 *Note: we may weaken these.* 403 *Note: we may weaken these.*
453 404
454 ### Await Expressions 405 ### Await Expressions
455 406
456 In an await expression of the form: 407 In an await expression of the form:
457 ```dart 408 ```dart
458 await expr 409 await expr
459 ``` 410 ```
460 strong mode requires `expr` to be a subtype of `Future`. In standard Dart, this is not required although tools may provide a hint. 411 strong mode requires `expr` to be a subtype of `Future`. In standard Dart, this is not required although tools may provide a hint.
Leaf 2016/01/14 01:07:17 This is no longer true - we allow await on Future<
vsm 2016/01/14 16:35:14 Is that the standard Dart rule as well? Should we
Leaf 2016/01/15 00:01:10 Other than the fact that we do downwards inference
461 412
462 ### Open Items 413 ### Open Items
414
415 TODO(vsm): Field overrides are now restricted.
463 416
464 We do not yet implement but are considering the following restrictions as well. 417 We do not yet implement but are considering the following restrictions as well.
465 418
466 - Disallow overriding fields: this results in complicated generated 419 - Disallow overriding fields: this results in complicated generated
467 code where a field definition in a subclass shadows the field 420 code where a field definition in a subclass shadows the field
468 definition in a base class but both are generally required to be 421 definition in a base class but both are generally required to be
469 allocated. Users should prefer explicit getters and setters in such 422 allocated. Users should prefer explicit getters and setters in such
470 cases. See [issue 52](https://github.com/dart-lang/dev_compiler/issues/52). 423 cases. See [issue 52](https://github.com/dart-lang/dev_compiler/issues/52).
471 424
472 - `Future<Future<T>>`: the Dart specification automatically flattens 425 - `Future<Future<T>>`: the Dart specification automatically flattens
473 this type to `Future<T>` (where `T` is not a `Future`). This can be 426 this type to `Future<T>` (where `T` is not a `Future`). This can be
474 issue as far as soundness. We are considering forbidding this type 427 issue as far as soundness. We are considering forbidding this type
475 altogether (with a combination of static and runtime checks). See 428 altogether (with a combination of static and runtime checks). See
476 [issue 228](https://github.com/dart-lang/dev_compiler/issues/228). 429 [issue 228](https://github.com/dart-lang/dev_compiler/issues/228).
477 430
431 ## DDC, Runtime checks, and Soundness
432
433 TODO(vsm): Move this to a different doc?
434
435 ### Runtime Checks
436
437 ### Type Narrowing
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698