Index: docs/language/informal/generalized-void.md |
diff --git a/docs/language/informal/generalized-void.md b/docs/language/informal/generalized-void.md |
index e18caa5b2494adcf347893b4f46cbcaaca66ab5c..23c45af326395fcac00133b8517d383eb91e78fc 100644 |
--- a/docs/language/informal/generalized-void.md |
+++ b/docs/language/informal/generalized-void.md |
@@ -30,28 +30,30 @@ covariantly. For instance, the class `Future<T>` uses return types |
like `Future<T>` and `Stream<T>`, and it uses `T` as a parameter type of a |
callback in the method `then`. |
-Note that is not technically dangerous to use a value of type `void`, it |
-does not violate any constraints at the level of the language semantics. |
-Developers just made the decision to declare that the value is useless, |
-based on the program logic. Hence, there is **no requirement** for the |
-generalized void mechanism to be strict and **sound**. However, it is the |
-intention that the mechanism should be sufficiently strict to make the |
-mechanism helpful and non-frustrating in practice. |
+Note that using the value of an expression of type `void` is not |
+technically dangerous, doing so does not violate any constraints at the |
+level of the language semantics. By using the type `void`, developers |
+indicate that the value of the corresponding expression evaluation is |
+meaningless. Hence, there is **no requirement** for the generalized void |
+mechanism to be strict and **sound**. However, it is the intention that the |
+mechanism should be sufficiently sound to make the mechanism helpful and |
+non-frustrating in practice. |
No constraints are imposed on which values may be given type `void`, so in |
that sense `void` can be considered to be just another name for the type |
-`Object`, flagged as useless. Note that this is an approximate rule (in |
-Dart 1.x), it fails to hold for function types. |
+`Object`, flagged as useless. Note that this is an approximate rule in |
+Dart 1.x, it fails to hold for function types; it does hold in Dart 2. |
-The mechanisms helping developers to avoid using values of type `void` are |
-divided into **two phases**. This document specifies the first phase. |
+The mechanisms helping developers to avoid using the value of an expression |
+of type `void` are divided into **two phases**. This document specifies the |
+first phase. |
The **first phase** uses restrictions which are based on syntactic criteria |
-in order to ensure that direct usage of a value of type `void` is a static |
-warning (in Dart 2: an error). A few exceptions are allowed, e.g., type |
-casts, such that developers can explicitly make the choice to use such a |
-value. The general rule is that all values of type `void` must be |
-discarded. |
+in order to ensure that direct usage of the value of an expression of type |
+`void` is a static warning (in Dart 2: an error). A few exceptions are |
+allowed, e.g., type casts, such that developers can explicitly make the |
+choice to use such a value. The general rule is that for every expression |
+of type `void`, its value must be ignored. |
The **second phase** will deal with casts and preservation of |
voidness. Some casts will cause derived expressions to switch from having |
@@ -62,7 +64,7 @@ example: |
```dart |
class A<T> { T foo(); } |
A<Object> a = new A<void>(); // Violates voidness preservation. |
-var x = a.foo(); // Use a "void value", with static type Object. |
+var x = a.foo(); // Use a "void value", now with static type Object. |
``` |
We intend to introduce a **voidness preservation analysis** (which is |
@@ -165,43 +167,39 @@ that formal type parameter can be the type void. That case is specified |
explicitly below. Apart from the reserved word `void` and a formal type |
parameter, no other term can denote the type void.* |
-*Conversely, `void` cannot denote any other entity than the type void: |
-`void` cannot occur as the declared name of any declaration (including |
-library prefixes, types, variables, parameters, etc.). This implies that |
-`void` is not subject to scoped lookup, and the name is not exported by any |
-system library. Similarly, it can never be accessed using a prefixed |
-expression (`p.void`). Hence, `void` has a fixed meaning everywhere in all |
-Dart programs, and it can only occur as a stand-alone word.* |
- |
-When `void` is passed as an actual type argument to a generic class or a |
-generic function, and when the type void occurs as a parameter type in a |
-function type, the reified representation is equal (according to `==`) to |
-the reified representation of the built-in class `Object`. |
- |
-*It is encouraged for an implementation to use a reified representation for |
-`void` as a type argument and as a parameter type in a function type which |
-is not `identical` to the reified representation of the built-in class |
-`Object`, but they must be equal. This allows implementations to produce |
-better diagnostic messages, e.g., in case of a runtime error.* |
+*There is no way for a Dart program at run time to obtain a reified |
+representation of a return type or parameter type of a function type, even |
+when the function type as a whole may be obtained (e.g., the function type |
+could be passed as a type argument and the corresponding formal type |
+parameter could be evaluated as an expression). A reified representation of |
+such a return type is therefore not necessary.* |
+ |
+For a composite type (a generic class instantiation or a function type), |
+the reified representation at run time must be such that the type void and |
+the built-in class `Object` are treated as equal according to `==`, but |
+they need not be `identical`. |
+ |
+*For example, with `typedef F<S, T> = S Function(T)`, the `Type` instance |
+for `F<Object, void>` at run time is `==` to the one for `F<void, void>` |
+and for `F<void, Object>`.* |
+ |
+*In case of a dynamic error, implementations are encouraged to emit an |
+error message that includes information about such parts of types being |
+`void` rather than `Object`. Developers will then see types which are |
+similar to the source code declarations. This may be achieved using |
+distinct `Type` objects to represent types such as `F<void, void>` and |
+`F<Object, void>`, comparing equal using `==` but not `identical`.* |
*This treatment of the reified representation of the type void reinforces |
the understanding that "voidness" is merely a statically known flag on the |
-built-in class `Object`, it is not a separate type. However, for backward |
-compatibility we need to treat return types differently.* |
- |
-When `void` is specified as the return type of a function, the reified |
-representation of the return type is left unspecified. |
- |
-*There is no way for a Dart program at run time to obtain a reified |
-representation of that return type alone, even when the function type as a |
-whole may be obtained (e.g., the function type could be evaluated as an |
-expression). It is therefore not necessary to reified representation of |
-such a return type.* |
+built-in class `Object`. However, for backward compatibility we need to |
+treat return types differently in Dart 1.x.* |
*It may be possible to use a reflective subsystem (mirrors) to deconstruct |
a function type whose return type is the type void, but the existing design |
of the system library `dart:mirrors` already handles this case by allowing |
-for a type mirror that does not have a reflected type.* |
+for a type mirror that does not have a reflected type. All in all, the type |
+void does not need to be reified at run time, and it is not reified.* |
Consider a type _T_ where the type void occurs as an actual type argument |
to a generic class, or as a parameter type in a function type. Dynamically, |
@@ -215,16 +213,18 @@ need to specify the the typing relations for generic function types. In |
Dart 2, the subtype relationship for generic function types follows from |
the rule that `void` is treated as `Object`.* |
-Consider a function type _T_ where the return type is the type void. The |
-dynamic more-specific-than relation, `<<`, and the dynamic subtype |
-relation, `<:`, are determined by the existing rules in the language |
-specification, supplemented by the above rule for handling occurrences of |
-the type void other than as a return type. |
+Consider a function type _T_ where the return type is the type void. In |
+Dart 1.x, the dynamic more-specific-than relation, `<<`, and the dynamic |
+subtype relation, `<:`, are determined by the existing rules in the |
+language specification, supplemented by the above rule for handling |
+occurrences of the type void other than as a return type. In Dart 2 there |
+is no exception for return types: the type void is treated as being the |
+built-in class `Object`. |
*This ensures backward compatibility for the cases where the type void can |
be used already today. It follows that it will be a breaking change to |
switch to a ruleset where the type void even as a return type is treated |
-like the built-in class Object, i.e. when switching to Dart 2.0. However, |
+like the built-in class Object, i.e. when switching to Dart 2. However, |
the only situation where the semantics differs is as follows: Consider a |
situation where a value of type `void Function(...)` is assigned to a |
variable or parameter `x` whose type annotation is `Object Function(...)`, |
@@ -251,20 +251,22 @@ class `Object`, or `dynamic`. |
For the static analysis, the more-specific-than relation, `<<`, and the |
subtype relation, `<:`, are determined by the same rules as described above |
-for the dynamic semantics. |
+for the dynamic semantics, for both Dart 1.x and Dart 2. |
*That is, the type void is considered to be equivalent to the built-in |
-class `Object`, except when used as a return type, in which case it is |
-effectively considered to be a proper supertype of `Object`. As mentioned, |
-voidness preservation is a separate analysis which is not specified by this |
-document, but it is intended to be used in the future to track "voidness" |
-in types and flag implicit casts wherein information about voidness may |
-indirectly be lost. With voidness preservation in place, we expect to be |
-able to treat the type void as `Object` in all cases during subtype |
-checks.* |
- |
-It is a static warning for an expression to have type void, except for the |
-following situations: |
+class `Object` in Dart 1.x, except when used as a return type, in which |
+case it is effectively considered to be a proper supertype of `Object`. In |
+Dart 2 subtyping, the type void is consistently considered to be equivalent |
+to the built-in class `Object`. As mentioned, this document does not |
+specify voidness preservation; however, when voidness preservation checks |
+are added we get an effect in Dart 2 which is similar to the special |
+treatment of void as a return type in Dart 1.x: The function type downcast |
+which will be rejected in Dart 1.x (at run time, with a static warning at |
+compile time) will become a voidness preservation violation, i.e., a |
+compile-time error.* |
+ |
+It is a static warning for an expression to have type void (in Dart 2: a |
+compile-time error), except for the following situations: |
* In an expressionStatement `e;`, e may have type void. |
* In the initialization and increment expressions of a for-loop, |
@@ -276,14 +278,28 @@ following situations: |
*Note that the parenthesized expression itself has type void, so it is |
again subject to the same constraints. Also note that we may not allow |
-return statements returning an expression of type void in the future, but |
+return statements returning an expression of type void in Dart 2, but |
it is allowed here for backward compatibility.* |
+*The value yielded by an expression of type void must be discarded (and |
+hence ignored), except when explicitly subjected to a type cast. This |
+"makes it hard to use a meaningless value", but leaves a small escape hatch |
+open for the cases where the developer knows that the typing misrepresents |
+the actual situation.* |
+ |
During bounds checking, it is possible that a bound of a formal type |
parameter of a generic class or function is statically known to be the type |
void. In this case, the bound is considered to be the built-in class |
`Object`. |
+In Dart 2, it is a compile-time error when a method declaration _D2_ with |
+return type void overrides a method declaration _D1_ whose return type is |
+not void. |
+ |
+*This rule is a special case of voidness preservation, which is needed in |
+order to maintain the discipline which arises naturally from the function |
+type subtype rules in Dart 1.x concerning void as a return type.* |
+ |
## Discussion |
Expressions derived from typeCast and typeTest do not support `void` as the |
@@ -291,8 +307,9 @@ target type. We have omitted support for this situation because we consider |
it to be useless. If void is passed indirectly via a type variable `T` then |
`e as T`, `e is T`, and `e is! T` will treat `T` like `Object`. In general, |
the rationale is that the type void admits all values (because it is just |
-`Object` plus a "static voidness flag"), but values of type void should be |
-discarded. |
+`Object` plus a "static voidness flag"), but the value of expressions of |
+type void should be discarded. So there is no point in *obtaining* the type |
+void for a given expression which already has a different type. |
The treatment of bounds is delicate. We syntactically prohibit `void` as a |
bound of a formal type parameter of a generic class or function. It is |
@@ -301,8 +318,8 @@ class, and that type argument might in turn be used as the bound of another |
formal type parameter of the class, or of a generic function in the |
class. It would be possible to make it a compile-time error to pass `void` |
as a type argument to a generic class where it will be used as a bound, but |
-this would presumably require a transitive traversal of all generic classes |
-and functions where the corresponding formal type parameter is passed on to |
+this would require a transitive traversal of all generic classes and |
+functions where the corresponding formal type parameter is passed on to |
other generic classes or functions, which would be highly brittle: A tiny |
change to a generic class or function could break code far away. So we do |
not wish to prevent formal type parameter bounds from indirectly becoming |
@@ -311,7 +328,13 @@ bound as `Object`. |
## Updates |
-* August 16h 2017: Removed exceptions allowing `e is T` and `e is! T`. |
+* August 22nd 2017: Reworded specification of reified types to deal with |
+ only such values which may be obtained at run time (previously mentioned |
+ some entities which may not exist). Added one override rule. |
+ |
+* August 17th 2017: Several parts clarified. |
+ |
+* August 16th 2017: Removed exceptions allowing `e is T` and `e is! T`. |
* August 9th 2017: Transferred to SDK repo, docs/language/informal. |
@@ -319,9 +342,9 @@ bound as `Object`. |
* June 13th 2017: Compile-time error for using a void value was changed to |
static warning. |
-* June 12th 2017: Grammar changed extensively, to use |
- `typeNotVoid` rather than |
- `voidOrType`. |
-* June 5th 2017: Added `typeCast` and |
- `typeTest` to the locations where void |
- expressions may occur. |
+ |
+* June 12th 2017: Grammar changed extensively, to use `typeNotVoid` |
+ rather than `voidOrType`. |
+ |
+* June 5th 2017: Added `typeCast` and `typeTest` to the locations where |
+ void expressions may occur. |