Index: STRONG_MODE.md |
diff --git a/STRONG_MODE.md b/STRONG_MODE.md |
index 490da83b3bdcfc154a18d32df198151a6252f8cb..a1caceb0a890bfe82154399a2e6936ab356f03a8 100644 |
--- a/STRONG_MODE.md |
+++ b/STRONG_MODE.md |
@@ -1,191 +1,50 @@ |
-# Strong Mode |
+# Strong Mode for Dart |
-## Overview |
+# Overview |
-The Dart Development Compiler (DDC) is a new Dart-to-JavaScript compiler that supports a large subset of the Dart programming language. DDC is motivated by the following goals: |
+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 traditionally typed languages. |
-First, we aim to generate clean, readable, consumable JavaScript output. This simplifies debugging Dart applications across multiple web platforms. It also enables better, seamless interoperability between Dart and JavaScript components. |
- |
-Second, we aim to compile in a fast, modular fashion. This enables a faster, better development cycle across a number of platforms and devices that lack native Dart support. It also allows Dart libraries to be packaged and distributed separately for use in other Dart or JavaScript applications. |
- |
-To accomplish these goals, we focus on a subset of Dart applications we can statically 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 strong mode will run the same in checked mode and, thus, in production mode. The subset we support entails: |
- |
- - A stricter, sounder, type system |
- - Type inference |
- - Restrictions on certain language constructs |
- |
-DDC is intended to complement our existing Dart2JS compiler. Unlike DDC, Dart2JS is focused on raw performance and support for the entire Dart language rather than readability, JavaScript interoperability, or modular compilation. |
- |
-This document provides a high-level overview of strong mode. A corresponding formalism of strong mode can also be found [here](https://dart-lang.github.io/dev_compiler/strong-dart.pdf). |
- |
-## Motivation |
- |
-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 running in checked mode. This allows considerable flexibility, but it also means that Dart implementations cannot easily use these annotations for optimization or code generation. |
- |
-Because of this, existing Dart implementations require dynamic dispatch. Furthermore, because Dart’s dispatch semantics are different from JavaScript’s, it effectively precludes mapping Dart calls to idiomatic JavaScript. For example, the following Dart code: |
+In particular, static type annotations are often misleading. Dart code such as: |
```dart |
-var x = a.bar; |
-b.foo("hello", x); |
+ var list = ["hello", "world"]; |
+ List<int> listOfInts = list; |
``` |
-cannot easily be mapped to the identical JavaScript code. If `a` does not contain a `bar` field, Dart requires a `NoSuchMethodError` while JavaScript simply returns undefined. If `b` contains a `foo` method, but with the wrong number of arguments, Dart again requires a `NoSuchMethodError` while JavaScript either ignores extra arguments or fills in omitted ones with undefined. |
- |
-To capture these differences, the Dart2JS compiler instead generates code that approximately looks like: |
+produces neither static nor runtime errors. Actual errors may show up much later on, e.g., with the following code, only at runtime on the invocation of `abs`: |
```dart |
-var x = getInterceptor(a).get$bar(a); |
-getInterceptor(b).foo$2(b, "hello", x); |
-``` |
-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 semantics. |
- |
-The above highlights why Dart-JavaScript interoperability hasn’t been seamless: Dart objects and methods do not look like normal JavaScript ones. |
- |
-DDC relies on strong mode to map Dart calling conventions to normal JavaScript ones. If `a` and `b` have static type annotations (with a type other than `dynamic`), strong mode statically verifies that they have a field `bar` and a 2-argument method `foo` respectively. In this case, DDC safely generates the identical JavaScript: |
- |
-```javascript |
-var x = a.bar; |
-b.foo("hello", x); |
+ Iterable<int> iterableOfInts = listOfInts.map((i) => i.abs()); |
``` |
-Note that DDC still supports the `dynamic` type, but relies on runtime helper functions in this case. E.g., if `a` and `b` are type `dynamic`, DDC instead generates: |
+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 programs that type check under a restricted set of rules. It statically rejects examples such as the above. |
-```javascript |
-var x = dload(a, "bar"); |
-dsend(b, "foo", "hello", x); |
-``` |
+To accomplish this, strong mode involves the following: |
-where `dload` and `dsend` are runtime helpers that implement Dart dispatch semantics. Programmers are encouraged to use static annotations to avoid this. Strong 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 argument and `a.bar`’s type as its second. If the code is sufficiently typed, runtime checks are unnecessary. |
+ - Type inference. Dart’s standard type rules treat omitted types as `dynamic` and effectively suppresses any static errors or warnings on variables of this type. Strong mode infers types based upon context. In the example above, strong mode infers that `list` has type `List`. |
-## Strong Mode Type System |
+ - Strict subtyping. Dart’s primary sources of unsoundness are due to its subtyping rules on function types and generic classes. Strong mode restricts these: e.g., `List` may not used as `List<int>` in the example above. |
-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 as our running example: |
+ - Generic methods. Standard Dart does not yet provide generic methods. This makes certain polymorphic methods difficult to use soundly. For example, the `List.map` invocation above is statically typed to return an `Iterable<dynamic>` in standard Dart. Strong mode allows methods to be annotated as generic. `List.map` 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 checked as generic in strong mode, and programmers may annotate their own methods as well. |
-```dart |
-library util; |
+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. |
-void info(List<int> list) { |
- var length = list.length; |
- if (length != 0) print("$length ${list[0]}"); |
-} |
-``` |
+# Usage |
-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 and checked mode would enforce this. |
+Strong mode is now integrated into the Dart Analyzer. The analyzer may be invoked in strong mode as follows: |
-However, in the following context, the info method prints “hello world” in checked mode, without any static errors or warnings: |
+ $ dartanalyzer --strong myapp.dart |
-```dart |
-import ‘dart:collection’; |
-import ‘utils.dart’; |
- |
-class MyList extends ListBase<int> implements List { |
- Object length; |
- |
- MyList(this.length); |
- |
- operator[](index) => "world"; |
- operator[]=(index, value) {} |
-} |
+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: |
-void main() { |
- List<int> list = new MyList("hello"); |
- info(list); |
-} |
``` |
- |
-The lack of static or runtime errors is not an oversight; it is by design. It provides developers a mechanism to circumvent or ignore types when convenient, but it comes at cost. While the above example is contrived, it demonstrates that developers cannot easily reason about a program modularly: the static type annotations in the `utils` library are of limited use, even in checked mode. |
- |
-For the same reason, a compiler cannot easily exploit type annotations if they are unsound. A Dart compiler cannot simply assume that a `List<int>` contains `int` values or even that its `length` is an integer. Instead, it must either rely on expensive (and often brittle) whole program analysis or on additional runtime checking. |
- |
-The fundamental issue above is that static annotations may not match runtime types, 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 compilers to rely on static types to reason about programs. |
- |
-Strong mode enforces the correctness of static type annotations. It simply disallows 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 by statically rejecting the declaration of `MyList`. This allows both the developer and the compiler to better reason about the info method. For statically checked code, both may assume that the argument is a proper `List<int>`, with integer-valued length and elements. |
- |
-DDC’s strong mode is strictly stronger than checked mode. A Dart program execution where (a) the program passes DDC’s static checking and (b) the execution does not trigger DDC’s runtime assertions, will also run in checked mode on any Dart platform. |
- |
- |
-### Static typing |
- |
-The primary sources of unsoundness in Dart are generics and functions. Both introduce circularity in the Dart subtyping relationship. |
- |
-#### Generics |
- |
-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 situations. For example, let *<:<sub>D</sub>* represent the standard Dart subtyping rule. Then, for all types `S` and `T`: |
- |
-`List<S>` <:<sub>D</sub> `List<dynamic>` <:<sub>D</sub> `List<T>` |
- |
-where `List` is equivalent to `List<dynamic>`. This introduces circularity - e.g.,: |
- |
-`List<int>` <:<sub>D</sub> `List` <:<sub>D</sub> `List<String>`<:<sub>D</sub> `List` <:<sub>D</sub> `List<int>` |
- |
- |
-From a programmer’s perspective, this means that, at compile-time, values that are 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 `List<String>` regardless of its actual values. |
- |
-Our running example exploits this. A `MyList` may be passed to the `info` function as it’s a subtype of the expected type: |
- |
-`MyList` <:<sub>D</sub> `List` <:<sub>D</sub>`List<int>` |
- |
-In strong mode, we introduce a stricter subtyping rule <:<sub>S</sub> to disallow this. In this case, in the context of a generic type parameter, dynamic may only serve as ⊤. This means that this is still true: |
- |
-`List<int>` <:<sub>S</sub> `List` |
- |
-but that this is not: |
- |
-`List<int>` ~~<:<sub>S</sub> `List`~~ |
- |
- |
-Our running example fails in strong mode: |
- |
-`MyList` <:<sub>S</sub> `List` ~~<:<sub>S</sub> `List<int>`~~ |
- |
- |
-#### Functions |
- |
-The other primary source of unsoundness in Dart is function subtyping. An unusual 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 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)). As with generics, this leads to circularity: |
- |
-`(int) -> int` <:<sub>D</sub> `(Object) -> Object` <:<sub>D</sub> `(int) -> int` |
- |
-And, as before, this can lead to surprising behavior. In Dart, an overridden method’s type should be a subtype of the base class method’s type (otherwise, a static warning is given). In our running example, the (implicit) `MyList.length` getter has type: |
- |
-`() -> Object` |
- |
-while the `List.length` getter it overrides has type: |
- |
-`() -> int` |
- |
-This is valid in standard Dart as: |
- |
-`() -> Object` <:<sub>D</sub> `() -> int` |
- |
-Because of this, a `length` that returns "hello" (a valid `Object`) triggers no static or runtime warnings or errors. |
- |
-Strong mode enforces the stricter, [traditional function subtyping](https://en.wikipedia.org/wiki/Subtyping#Function_types) rule: subtyping is contravariant in parameter types and covariant in return types. This permits: |
- |
-`() -> int` <:<sub>S</sub> `() -> Object` |
- |
-but disallows: |
- |
-`() -> Object` <:<sub>S</sub> `() -> int` |
- |
-With respect to our example, strong mode requires that any subtype of a List have an int-typed length. It statically rejects the length declaration in MyList. |
- |
-Formal details of the strong mode type system may be found [here](https://dart-lang.github.io/dev_compiler/strong-dart.pdf). |
- |
-### Implicit runtime assertions |
- |
-Although strong mode relies heavily on static checking, it also requires some runtime checking for soundness. For example, the following code is allowed: |
- |
-```dart |
-dynamic x = …; |
-List y = x; |
-int z = y.length; |
+analyzer: |
+ strong-mode: true |
``` |
-but a runtime check is required (and inserted by DDC) to ensure that `y` is assigned a `List`. This is similar to checked mode, but much less pervasive. Checked mode would also require a runtime check on the assignment of `z`. Strong mode would not as this is enforced statically instead. |
+# Type Inference |
-## Type Inference |
- |
-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 as lightweight as possible. |
+An explicit goal of strong mode to provide stronger typing while preserving the terseness of Dart. While strong mode requires and/or encourages more static type annotations, our aim is make this as lightweight as possible. |
Bob Nystrom
2015/12/22 21:30:58
I would omit the last sentence. I don't think stro
vsm
2016/01/13 23:00:20
Reworked this.
|
In Dart, per the specification, the static type of a variable `x` declared as: |
@@ -195,16 +54,18 @@ var x = <String, String>{ "hello": "world"}; |
is `dynamic` as there is no explicit type annotation on the left-hand side. To discourage code bloat, the Dart style guide generally recommends omitting these type annotations in many situations. In these cases, the benefits of strong mode would be lost. |
-To avoid this, strong mode uses limited inference. In the case above, the strong mode 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 other type. To maximize the impact, we perform the following inference in the following order: |
+To avoid this, strong mode uses type inference. In the case above, the strong mode 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 other type. To maximize the impact, we perform the following inference in the following order: |
- Top-level and static fields |
- Instance fields and methods |
- Local variables |
- Allocation expressions |
+TODO(vsm): Move runtime discussion |
+ |
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 effect of this inference (other than stricter type errors) should not be observable at runtime outside the use of the mirrors API or by explicitly examining Object.runtimeType. (Note, in the next section, we discuss corresponding restrictions on `is` and `as` type checks.) |
-### Top-level and Static Fields |
+###. Top-level and Static Fields |
Bob Nystrom
2015/12/22 21:30:58
?
vsm
2016/01/13 23:00:20
Ugh. Emacs markdown mode sucks. Switching to a d
|
Strong mode will infer the static type of any top-level or static field with: |
@@ -232,13 +93,7 @@ var TAU = _PI_FIRST ? PI * 2 : 6.28318; |
the variables `PI` and `TAU` are circularly dependent on each other. Strong mode would leave the static type of both as `dynamic`. |
-<em> |
-Note - we’re experimenting with a few arguably simpler variants here: |
-- Limiting inference to final or const fields (i.e., not var). |
-- Limiting transitive inference to explicit program order. |
-</em> |
- |
-### Instance Fields and Methods |
+###. Instance Fields and Methods |
Strong mode performs two types of inference on instances fields and methods. |
@@ -251,7 +106,7 @@ abstract class A { |
} |
class B extends A { |
- var m; |
+ var m; |
value(i) => m[i]; |
… |
} |
@@ -266,12 +121,12 @@ However, rather than rejecting the above code, strong mode employs inference to |
```dart |
class B extends A { |
Map m; |
- int value(i) => m[i]; |
+ int value(int i) => m[i]; |
… |
} |
``` |
-Note that the argument type of `value` is left as `dynamic`. Tightening this type is not required for soundness. |
+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 explicitly type this as `dynamic` or `Object` to avoid inferring the narrower type. |
The second form inference is limited to instance fields (not methods) and is similar to that on static fields. For instance fields where the static type is omitted and an initializer is present, the field’s type is inferred as the initializer’s type. In this continuation of our example: |
@@ -282,16 +137,10 @@ class C extends A { |
... |
} |
``` |
-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 the corresponding declaration in `A`. |
-<em> |
-Note - we’re considering with a few variants here as well: |
-- Limiting inference to final or const fields (i.e., not var). |
-- Inference on parameter types when omitted (e.g., the argument to `B.value` above). |
-- When to allow or prefer override-based inference. |
-</em> |
+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 the corresponding declaration in `A`. |
-### Local Variables |
+###. Local Variables |
As with fields, local variable types are inferred if the static type is omitted and an initializer expression is present. In the following example: |
@@ -309,11 +158,11 @@ z = “$z”; |
``` |
would trigger a static error in strong mode, but is allowed in standard Dart. In strong mode, the programmer must use an explicit type annotation to avoid inference. Explicitly declaring `z` with the type `Object` or `dynamic` would suffice in this case. |
-### Allocation Expressions |
+###. Allocation Expressions |
The final form of strong mode inference is on allocation expressions. This inference is rather different from the above: it tightens the runtime type of the corresponding expression using the static type of its context. Contextual inference 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`). |
-#### Closure literals |
+####.. Closure literals |
Consider the following example: |
@@ -335,7 +184,7 @@ Dart has a syntactic limitation in this case: it is not possible to statically a |
Strong mode sidesteps this difficulty via contextual inference. It infers the closure type as `(dynamic) -> int`. This is the most general type allowed by the context: the parameter type of apply. |
-#### List and map literals |
+####.. List and map literals |
Similarly, strong mode infers tighter runtime types for list and map literals. E.g., in |
@@ -365,7 +214,7 @@ Map<List<String>, Map<int, int>> map = |
as "world" is not of type `int`. |
-#### Constructor invocations |
+####.. Constructor invocations |
Finally, strong mode performs similar contextual inference on explicit constructor invocations via new or const. For example: |
@@ -382,17 +231,121 @@ Set<String> string = |
Note, as above, context is propagated downward into the expression. |
-## General Language Restrictions |
+# Strict subtyping |
+The primary sources of unsoundness in Dart are generics and functions. Both introduce circularity in the Dart subtyping relationship. |
Bob Nystrom
2015/12/22 21:30:58
Not implicit downcasting?
vsm
2016/01/13 23:00:20
No, that doesn't affect the underlying type system
|
+ |
+####. Generics |
+ |
+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 situations. For example, let *<:<sub>D</sub>* represent the standard Dart subtyping rule. Then, for all types `S` and `T`: |
Bob Nystrom
2015/12/22 21:30:58
No hyphen in "covariant".
|
+ |
+`List<S>` <:<sub>D</sub> `List<dynamic>` <:<sub>D</sub> `List<T>` |
+ |
+where `List` is equivalent to `List<dynamic>`. This introduces circularity - e.g.,: |
+ |
+`List<int>` <:<sub>D</sub> `List` <:<sub>D</sub> `List<String>`<:<sub>D</sub> `List` <:<sub>D</sub> `List<int>` |
+ |
+ |
+From a programmer’s perspective, this means that, at compile-time, values that are 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 `List<String>` regardless of its actual values. |
+ |
+Our running example exploits this. A `MyList` may be passed to the `info` function as it’s a subtype of the expected type: |
+ |
+`MyList` <:<sub>D</sub> `List` <:<sub>D</sub>`List<int>` |
+ |
+In strong mode, we introduce a stricter subtyping rule <:<sub>S</sub> to disallow this. In this case, in the context of a generic type parameter, dynamic may only serve as ⊤. This means that this is still true: |
+ |
+`List<int>` <:<sub>S</sub> `List` |
+ |
+but that this is not: |
+ |
+`List<int>` ~~<:<sub>S</sub> `List`~~ |
+ |
+ |
+Our running example fails in strong mode: |
+ |
+`MyList` <:<sub>S</sub> `List` ~~<:<sub>S</sub> `List<int>`~~ |
+ |
+ |
+####. Functions |
+ |
+The other primary source of unsoundness in Dart is function subtyping. An unusual 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 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)). As with generics, this leads to circularity: |
+ |
+`(int) -> int` <:<sub>D</sub> `(Object) -> Object` <:<sub>D</sub> `(int) -> int` |
+ |
+And, as before, this can lead to surprising behavior. In Dart, an overridden method’s type should be a subtype of the base class method’s type (otherwise, a static warning is given). In our running example, the (implicit) `MyList.length` getter has type: |
+ |
+`() -> Object` |
+ |
+while the `List.length` getter it overrides has type: |
+ |
+`() -> int` |
+ |
+This is valid in standard Dart as: |
+ |
+`() -> Object` <:<sub>D</sub> `() -> int` |
+ |
+Because of this, a `length` that returns "hello" (a valid `Object`) triggers no static or runtime warnings or errors. |
+ |
+Strong mode enforces the stricter, [traditional function subtyping](https://en.wikipedia.org/wiki/Subtyping#Function_types) rule: subtyping is contravariant in parameter types and covariant in return types. This permits: |
+ |
+`() -> int` <:<sub>S</sub> `() -> Object` |
+ |
+but disallows: |
+ |
+`() -> Object` <:<sub>S</sub> `() -> int` |
+ |
+With respect to our example, strong mode requires that any subtype of a List have an int-typed length. It statically rejects the length declaration in MyList. |
+ |
+Formal details of the strong mode type system may be found [here](https://dart-lang.github.io/dev_compiler/strong-dart.pdf). |
+ |
+# Generic Methods |
+ |
+Strong mode introduces generic methods to allow stricter typing on polymorphic methods. Such code in standard Dart today often loses static type information. For example, the `Iterable.map` method is declared as below: |
-In addition to stricter typing rules, DDC enforces other restrictions on Dart programs. |
+```dart |
+abstract class Iterable<E> { |
+ ... |
+ Iterable map(f(E e)); |
+} |
+``` |
+ |
+Regardless of the static type of the function `f`, the `map` always returns an `Iterable<dynamic>`. |
+ |
+TODO(vsm): Link to DEP. |
+ |
+Generic methods have been proposed as an addition to the Dart language. To enable experimentation, strong mode provides a comment-based form of generic methods based on the existing DEP, but usable on all existing Dart implementations. The `Iterable.map` method is declared as follows: |
+ |
+```dart |
+abstract class Iterable<E> { |
+ ... |
+ Iterable/*<T>*/ map/*<T>*/(/*=T*/ f(E e)); |
+} |
+``` |
+ |
+At a use site, the generic type may be explicitly provided or inferred from context: |
+ |
+``` |
+ var l = <int>[1, 2, 3]; |
+ var i1 = l.map((i) => i + 1); |
+ var l2 = l.map/*<String>*/((i) { ... }); |
+``` |
-### Warnings are Errors |
+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 result, `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. `i2` is typed as `Iterable<String>`. |
-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 missing methods required by a declared interface). A full list of Dart static warnings 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: |
+ |
+# Additional Restrictions |
+ |
+In addition to stricter typing rules, strong mode enforces other |
+restrictions on Dart programs. |
+ |
+####. Warnings as Errors |
+ |
+Strong mode 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 missing methods required by a declared interface). A full list of Dart static warnings 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: |
[https://github.com/dart-lang/sdk/blob/master/pkg/analyzer/lib/src/generated/error.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%23L3772&sa=D&sntz=1&usg=AFQjCNFc4E37M1PshVcw4zk7C9jXgqfGbw) |
-### Is / As Restrictions |
+####. Is / As Restrictions |
+ |
+TODO(vsm): Fix this |
Dart is and as runtime checks expose the unsoundness of the type system in certain cases. For example, consider: |
@@ -407,7 +360,7 @@ if (list is List<int>) { |
Perhaps surprisingly, the first test - `list is List<int>` - evaluates to true here. Such code is highly likely to be erroneous. |
-DDC strong mode statically disallows problematic `is` or `as` checks.. In general, an expression: |
+Strong mode statically disallows problematic `is` or `as` checks.. In general, an expression: |
```dart |
x is T |
@@ -427,13 +380,11 @@ is only allowed where `T` is a *ground type*: |
In all other cases, strong mode reports a static error. |
-### Super Invocations |
- |
-In the context of constructor initializer lists, DDC restricts `super` invocations to the end. This restriction simplifies generated code with minimal effect on the program. |
+####. Super Invocations |
-*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.* |
+In the context of constructor initializer lists, strong mode restricts `super` invocations to the end. This restriction simplifies generated code with minimal effect on the program. |
-### For-in Loops |
+####. For-in loops |
In for-in statements of the form: |
@@ -451,7 +402,7 @@ the expression `e` is required to be an `Iterable<T>`. |
*Note: we may weaken these.* |
-### Await Expressions |
+####. Await Expressions |
In an await expression of the form: |
```dart |
@@ -461,6 +412,8 @@ strong mode requires `expr` to be a subtype of `Future`. In standard Dart, this |
### Open Items |
+TODO(vsm): Field overrides are now restricted. |
+ |
We do not yet implement but are considering the following restrictions as well. |
- Disallow overriding fields: this results in complicated generated |
@@ -475,3 +428,11 @@ We do not yet implement but are considering the following restrictions as well. |
altogether (with a combination of static and runtime checks). See |
[issue 228](https://github.com/dart-lang/dev_compiler/issues/228). |
+# DDC, Runtime checks, and Soundness |
+ |
+TODO(vsm): Move runtime behavior here. |
+ |
+## Runtime Checks |
+ |
+## Type Narrowing |
+ |