Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 ## Feature: Generalized Void | 1 ## Feature: Generalized Void |
| 2 | 2 |
| 3 Author: eernst@ | 3 Author: eernst@ |
| 4 | 4 |
| 5 **Status**: Under implementation. | 5 **Status**: Under implementation. |
| 6 | 6 |
| 7 **This document** is an informal specification of the generalized support | 7 **This document** is an informal specification of the generalized support |
| 8 in Dart 1.x for the type `void`. Dart 2 will have a very similar kind of | 8 in Dart 1.x for the type `void`. Dart 2 will have a very similar kind of |
| 9 generalized support for `void`, without the function type subtype exception | 9 generalized support for `void`, without the function type subtype exception |
| 10 that this feature includes for backward compatibility in Dart 1.x. This | 10 that this feature includes for backward compatibility in Dart 1.x. This |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 used to indicate that the visit is performed for its side-effects | 23 used to indicate that the visit is performed for its side-effects |
| 24 alone. The generalized void feature includes mechanisms to help developers | 24 alone. The generalized void feature includes mechanisms to help developers |
| 25 avoid using such a value. | 25 avoid using such a value. |
| 26 | 26 |
| 27 In general, situations where it may be desirable to use `void` as | 27 In general, situations where it may be desirable to use `void` as |
| 28 a type argument arise when the corresponding formal type variable is used | 28 a type argument arise when the corresponding formal type variable is used |
| 29 covariantly. For instance, the class `Future<T>` uses return types | 29 covariantly. For instance, the class `Future<T>` uses return types |
| 30 like `Future<T>` and `Stream<T>`, and it uses `T` as a parameter type of a | 30 like `Future<T>` and `Stream<T>`, and it uses `T` as a parameter type of a |
| 31 callback in the method `then`. | 31 callback in the method `then`. |
| 32 | 32 |
| 33 Note that is not technically dangerous to use a value of type `void`, it | 33 Note that it is not technically dangerous to use a value of type `void`, |
| 34 does not violate any constraints at the level of the language semantics. | 34 doing so does not violate any constraints at the level of the language |
| 35 Developers just made the decision to declare that the value is useless, | 35 semantics. By using the type `void`, developers just indicate that the |
| 36 based on the program logic. Hence, there is **no requirement** for the | 36 value of the corresponding expression evaluation is useless. Hence, there |
|
Lasse Reichstein Nielsen
2017/08/22 05:31:06
Maybe: "useless and unspecified"
eernst
2017/08/22 09:09:46
Adjusted to 'meaningless'.
| |
| 37 generalized void mechanism to be strict and **sound**. However, it is the | 37 is **no requirement** for the generalized void mechanism to be strict and |
| 38 intention that the mechanism should be sufficiently strict to make the | 38 **sound**. However, it is the intention that the mechanism should be |
| 39 mechanism helpful and non-frustrating in practice. | 39 sufficiently sound to make the mechanism helpful and non-frustrating in |
| 40 practice. | |
| 40 | 41 |
| 41 No constraints are imposed on which values may be given type `void`, so in | 42 No constraints are imposed on which values may be given type `void`, so in |
| 42 that sense `void` can be considered to be just another name for the type | 43 that sense `void` can be considered to be just another name for the type |
| 43 `Object`, flagged as useless. Note that this is an approximate rule (in | 44 `Object`, flagged as useless. Note that this is an approximate rule in |
| 44 Dart 1.x), it fails to hold for function types. | 45 Dart 1.x, it fails to hold for function types; it does hold in Dart 2. |
| 45 | 46 |
| 46 The mechanisms helping developers to avoid using values of type `void` are | 47 The mechanisms helping developers to avoid using values of type `void` are |
| 47 divided into **two phases**. This document specifies the first phase. | 48 divided into **two phases**. This document specifies the first phase. |
| 48 | 49 |
| 49 The **first phase** uses restrictions which are based on syntactic criteria | 50 The **first phase** uses restrictions which are based on syntactic criteria |
| 50 in order to ensure that direct usage of a value of type `void` is a static | 51 in order to ensure that direct usage of a value of type `void` is a static |
| 51 warning (in Dart 2: an error). A few exceptions are allowed, e.g., type | 52 warning (in Dart 2: an error). A few exceptions are allowed, e.g., type |
| 52 casts, such that developers can explicitly make the choice to use such a | 53 casts, such that developers can explicitly make the choice to use such a |
| 53 value. The general rule is that all values of type `void` must be | 54 value. The general rule is that all values of type `void` must be |
| 54 discarded. | 55 ignored. |
| 55 | 56 |
| 56 The **second phase** will deal with casts and preservation of | 57 The **second phase** will deal with casts and preservation of |
| 57 voidness. Some casts will cause derived expressions to switch from having | 58 voidness. Some casts will cause derived expressions to switch from having |
| 58 type `void` to having some other type, and hence those casts introduce the | 59 type `void` to having some other type, and hence those casts introduce the |
| 59 possibility that "a void value" will get passed and used. Here is an | 60 possibility that "a void value" will get passed and used. Here is an |
| 60 example: | 61 example: |
| 61 | 62 |
| 62 ```dart | 63 ```dart |
| 63 class A<T> { T foo(); } | 64 class A<T> { T foo(); } |
| 64 A<Object> a = new A<void>(); // Violates voidness preservation. | 65 A<Object> a = new A<void>(); // Violates voidness preservation. |
| 65 var x = a.foo(); // Use a "void value", with static type Object. | 66 var x = a.foo(); // Use a "void value", now with static type Object. |
| 66 ``` | 67 ``` |
| 67 | 68 |
| 68 We intend to introduce a **voidness preservation analysis** (which is | 69 We intend to introduce a **voidness preservation analysis** (which is |
| 69 similar to a small type system) to keep track of such situations. As | 70 similar to a small type system) to keep track of such situations. As |
| 70 mentioned, the second phase is **not specified in this document**. Voidness | 71 mentioned, the second phase is **not specified in this document**. Voidness |
| 71 preservation is a purely static analysis, and there are no plans to | 72 preservation is a purely static analysis, and there are no plans to |
| 72 introduce dynamic checking for it. | 73 introduce dynamic checking for it. |
| 73 | 74 |
| 74 ## Syntax | 75 ## Syntax |
| 75 | 76 |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 | 168 |
| 168 *Conversely, `void` cannot denote any other entity than the type void: | 169 *Conversely, `void` cannot denote any other entity than the type void: |
| 169 `void` cannot occur as the declared name of any declaration (including | 170 `void` cannot occur as the declared name of any declaration (including |
| 170 library prefixes, types, variables, parameters, etc.). This implies that | 171 library prefixes, types, variables, parameters, etc.). This implies that |
| 171 `void` is not subject to scoped lookup, and the name is not exported by any | 172 `void` is not subject to scoped lookup, and the name is not exported by any |
| 172 system library. Similarly, it can never be accessed using a prefixed | 173 system library. Similarly, it can never be accessed using a prefixed |
| 173 expression (`p.void`). Hence, `void` has a fixed meaning everywhere in all | 174 expression (`p.void`). Hence, `void` has a fixed meaning everywhere in all |
| 174 Dart programs, and it can only occur as a stand-alone word.* | 175 Dart programs, and it can only occur as a stand-alone word.* |
| 175 | 176 |
| 176 When `void` is passed as an actual type argument to a generic class or a | 177 When `void` is passed as an actual type argument to a generic class or a |
| 177 generic function, and when the type void occurs as a parameter type in a | 178 generic function invocation, and when the type void occurs as a parameter |
| 178 function type, the reified representation is equal (according to `==`) to | 179 type in a function type, the reified representation is equal (according to |
|
Lasse Reichstein Nielsen
2017/08/22 05:31:06
Consider: equal -> equivalent
We use "equivalent"
Lasse Reichstein Nielsen
2017/08/22 05:31:07
What does it mean to "occur as a parameter type" i
eernst
2017/08/22 09:09:46
Done.
| |
| 179 the reified representation of the built-in class `Object`. | 180 `==`) to the reified representation of the built-in class `Object`. |
| 180 | 181 |
| 181 *It is encouraged for an implementation to use a reified representation for | 182 *It is encouraged for an implementation to use a reified representation for |
|
Lasse Reichstein Nielsen
2017/08/22 05:31:06
Wording, consider: "An implementation is encourage
| |
| 182 `void` as a type argument and as a parameter type in a function type which | 183 `void` as a type argument and as a parameter type in a function type which |
| 183 is not `identical` to the reified representation of the built-in class | 184 is not `identical` to the reified representation of the built-in class |
| 184 `Object`, but they must be equal. This allows implementations to produce | 185 `Object`, but they must be equal. This allows implementations to produce |
| 185 better diagnostic messages, e.g., in case of a runtime error.* | 186 better diagnostic messages, e.g., in case of a runtime error.* |
| 186 | 187 |
| 187 *This treatment of the reified representation of the type void reinforces | 188 *This treatment of the reified representation of the type void reinforces |
| 188 the understanding that "voidness" is merely a statically known flag on the | 189 the understanding that "voidness" is merely a statically known flag on the |
| 189 built-in class `Object`, it is not a separate type. However, for backward | 190 built-in class `Object`, it is not a separate type. However, for backward |
| 190 compatibility we need to treat return types differently.* | 191 compatibility we need to treat return types differently.* |
| 191 | 192 |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 217 | 218 |
| 218 Consider a function type _T_ where the return type is the type void. The | 219 Consider a function type _T_ where the return type is the type void. The |
| 219 dynamic more-specific-than relation, `<<`, and the dynamic subtype | 220 dynamic more-specific-than relation, `<<`, and the dynamic subtype |
| 220 relation, `<:`, are determined by the existing rules in the language | 221 relation, `<:`, are determined by the existing rules in the language |
| 221 specification, supplemented by the above rule for handling occurrences of | 222 specification, supplemented by the above rule for handling occurrences of |
| 222 the type void other than as a return type. | 223 the type void other than as a return type. |
| 223 | 224 |
| 224 *This ensures backward compatibility for the cases where the type void can | 225 *This ensures backward compatibility for the cases where the type void can |
| 225 be used already today. It follows that it will be a breaking change to | 226 be used already today. It follows that it will be a breaking change to |
| 226 switch to a ruleset where the type void even as a return type is treated | 227 switch to a ruleset where the type void even as a return type is treated |
| 227 like the built-in class Object, i.e. when switching to Dart 2.0. However, | 228 like the built-in class Object, i.e. when switching to Dart 2. However, |
| 228 the only situation where the semantics differs is as follows: Consider a | 229 the only situation where the semantics differs is as follows: Consider a |
| 229 situation where a value of type `void Function(...)` is assigned to a | 230 situation where a value of type `void Function(...)` is assigned to a |
| 230 variable or parameter `x` whose type annotation is `Object Function(...)`, | 231 variable or parameter `x` whose type annotation is `Object Function(...)`, |
| 231 where the argument types are arbitrary, but such that the assignment is | 232 where the argument types are arbitrary, but such that the assignment is |
| 232 permitted. In that situation, in checked mode, the assignment will fail | 233 permitted. In that situation, in checked mode, the assignment will fail |
| 233 with the current semantics, because the type of that value is not a subtype | 234 with the current semantics, because the type of that value is not a subtype |
| 234 of the type of `x`. The rules in this document preserve that behavior. If | 235 of the type of `x`. The rules in this document preserve that behavior. If |
| 235 we were to consistently treat the type void as `Object` at run time (as in | 236 we were to consistently treat the type void as `Object` at run time (as in |
| 236 Dart 2) then this assignment would be permitted (but we would then use | 237 Dart 2) then this assignment would be permitted (but we would then use |
| 237 voidness preservation to detect and avoid this situation at compile time).* | 238 voidness preservation to detect and avoid this situation at compile time).* |
| 238 | 239 |
| 239 *The semantics of checked mode checks involving types where the type void | 240 *The semantics of checked mode checks involving types where the type void |
| 240 occurs is determined by the semantics of subtype tests, so we do not | 241 occurs is determined by the semantics of subtype tests, so we do not |
| 241 specify that separately.* | 242 specify that separately.* |
| 242 | 243 |
| 243 An instantiation of a generic class `G` is malbounded if it contains `void` | 244 An instantiation of a generic class `G` is malbounded if it contains `void` |
| 244 as an actual type argument for a formal type parameter, unless that type | 245 as an actual type argument for a formal type parameter, unless that type |
| 245 parameter does not have a bound, or it has a bound which is the built-in | 246 parameter does not have a bound, or it has a bound which is the built-in |
| 246 class `Object`, or `dynamic`. | 247 class `Object`, or `dynamic`. |
| 247 | 248 |
| 248 *The treatment of malbounded types follows the current specification.* | 249 *The treatment of malbounded types follows the current specification.* |
| 249 | 250 |
| 250 ## Static Analysis | 251 ## Static Analysis |
| 251 | 252 |
| 252 For the static analysis, the more-specific-than relation, `<<`, and the | 253 For the static analysis, the more-specific-than relation, `<<`, and the |
| 253 subtype relation, `<:`, are determined by the same rules as described above | 254 subtype relation, `<:`, are determined by the same rules as described above |
| 254 for the dynamic semantics. | 255 for the dynamic semantics. |
| 255 | 256 |
| 256 *That is, the type void is considered to be equivalent to the built-in | 257 *That is, the type void is considered to be equivalent to the built-in |
|
Lasse Reichstein Nielsen
2017/08/22 05:31:06
Here we use "equivalent".
| |
| 257 class `Object`, except when used as a return type, in which case it is | 258 class `Object` in Dart 1.x, except when used as a return type, in which |
| 258 effectively considered to be a proper supertype of `Object`. As mentioned, | 259 case it is effectively considered to be a proper supertype of `Object`. In |
| 259 voidness preservation is a separate analysis which is not specified by this | 260 Dart 2 subtyping, the type void is consistently considered to be equivalent |
| 260 document, but it is intended to be used in the future to track "voidness" | 261 to the built-in class `Object`. As mentioned, this document does not |
| 261 in types and flag implicit casts wherein information about voidness may | 262 specify voidness preservation; however, when voidness preservation checks |
| 262 indirectly be lost. With voidness preservation in place, we expect to be | 263 are added we get an effect in Dart 2 which is similar to the special |
| 263 able to treat the type void as `Object` in all cases during subtype | 264 treatment of void as a return type in Dart 1.x: The function type downcast |
| 264 checks.* | 265 which will be rejected in Dart 1.x (at run time, with a static warning at |
| 266 compile time) will become a voidness preservation violation, i.e., a | |
| 267 compile-time error.* | |
| 265 | 268 |
| 266 It is a static warning for an expression to have type void, except for the | 269 It is a static warning for an expression to have type void (in Dart 2: a |
| 267 following situations: | 270 compile-time error), except for the following situations: |
| 268 | 271 |
| 269 * In an expressionStatement `e;`, e may have type void. | 272 * In an expressionStatement `e;`, e may have type void. |
| 270 * In the initialization and increment expressions of a for-loop, | 273 * In the initialization and increment expressions of a for-loop, |
| 271 `for (e1; e2; e3) {..}`, `e1` and `e3` may have type void. | 274 `for (e1; e2; e3) {..}`, `e1` and `e3` may have type void. |
| 272 * In a typeCast `e as T`, `e` may have type void. | 275 * In a typeCast `e as T`, `e` may have type void. |
| 273 * In a parenthesized expression `(e)`, `e` may have type void. | 276 * In a parenthesized expression `(e)`, `e` may have type void. |
| 274 * In a return statement `return e;`, when the return type of the innermost | 277 * In a return statement `return e;`, when the return type of the innermost |
| 275 enclosing function is the type void, `e` may have type void. | 278 enclosing function is the type void, `e` may have type void. |
| 276 | 279 |
| 277 *Note that the parenthesized expression itself has type void, so it is | 280 *Note that the parenthesized expression itself has type void, so it is |
| 278 again subject to the same constraints. Also note that we may not allow | 281 again subject to the same constraints. Also note that we may not allow |
| 279 return statements returning an expression of type void in the future, but | 282 return statements returning an expression of type void in Dart 2, but |
| 280 it is allowed here for backward compatibility.* | 283 it is allowed here for backward compatibility.* |
| 281 | 284 |
| 285 *The value yielded by an expression of type void must be discarded (and | |
| 286 hence ignored), except when explicitly subjected to a type cast. This | |
| 287 "makes it hard to use useless values", but leaves a small escape hatch open | |
| 288 for the cases where the developer knows that the typing misrepresents the | |
| 289 actual situation.* | |
| 290 | |
| 282 During bounds checking, it is possible that a bound of a formal type | 291 During bounds checking, it is possible that a bound of a formal type |
| 283 parameter of a generic class or function is statically known to be the type | 292 parameter of a generic class or function is statically known to be the type |
| 284 void. In this case, the bound is considered to be the built-in class | 293 void. In this case, the bound is considered to be the built-in class |
| 285 `Object`. | 294 `Object`. |
| 286 | 295 |
| 287 ## Discussion | 296 ## Discussion |
| 288 | 297 |
| 289 Expressions derived from typeCast and typeTest do not support `void` as the | 298 Expressions derived from typeCast and typeTest do not support `void` as the |
| 290 target type. We have omitted support for this situation because we consider | 299 target type. We have omitted support for this situation because we consider |
| 291 it to be useless. If void is passed indirectly via a type variable `T` then | 300 it to be useless. If void is passed indirectly via a type variable `T` then |
| 292 `e as T`, `e is T`, and `e is! T` will treat `T` like `Object`. In general, | 301 `e as T`, `e is T`, and `e is! T` will treat `T` like `Object`. In general, |
| 293 the rationale is that the type void admits all values (because it is just | 302 the rationale is that the type void admits all values (because it is just |
| 294 `Object` plus a "static voidness flag"), but values of type void should be | 303 `Object` plus a "static voidness flag"), but values of type void should be |
| 295 discarded. | 304 discarded. So there is no point in *obtaining* the type void for a given |
| 305 expression which already has a different type. | |
| 296 | 306 |
| 297 The treatment of bounds is delicate. We syntactically prohibit `void` as a | 307 The treatment of bounds is delicate. We syntactically prohibit `void` as a |
| 298 bound of a formal type parameter of a generic class or function. It is | 308 bound of a formal type parameter of a generic class or function. It is |
| 299 possible to pass the type void as an actual type argument to a generic | 309 possible to pass the type void as an actual type argument to a generic |
| 300 class, and that type argument might in turn be used as the bound of another | 310 class, and that type argument might in turn be used as the bound of another |
| 301 formal type parameter of the class, or of a generic function in the | 311 formal type parameter of the class, or of a generic function in the |
| 302 class. It would be possible to make it a compile-time error to pass `void` | 312 class. It would be possible to make it a compile-time error to pass `void` |
| 303 as a type argument to a generic class where it will be used as a bound, but | 313 as a type argument to a generic class where it will be used as a bound, but |
| 304 this would presumably require a transitive traversal of all generic classes | 314 this would require a transitive traversal of all generic classes and |
| 305 and functions where the corresponding formal type parameter is passed on to | 315 functions where the corresponding formal type parameter is passed on to |
| 306 other generic classes or functions, which would be highly brittle: A tiny | 316 other generic classes or functions, which would be highly brittle: A tiny |
| 307 change to a generic class or function could break code far away. So we do | 317 change to a generic class or function could break code far away. So we do |
| 308 not wish to prevent formal type parameter bounds from indirectly becoming | 318 not wish to prevent formal type parameter bounds from indirectly becoming |
| 309 the type void. This motivated the decision to treat such a void-valued | 319 the type void. This motivated the decision to treat such a void-valued |
| 310 bound as `Object`. | 320 bound as `Object`. |
| 311 | 321 |
| 312 ## Updates | 322 ## Updates |
| 313 | 323 |
| 314 * August 16h 2017: Removed exceptions allowing `e is T` and `e is! T`. | 324 * August 17th 2017: Several parts clarified. |
| 325 | |
| 326 * August 16th 2017: Removed exceptions allowing `e is T` and `e is! T`. | |
| 315 | 327 |
| 316 * August 9th 2017: Transferred to SDK repo, docs/language/informal. | 328 * August 9th 2017: Transferred to SDK repo, docs/language/informal. |
| 317 | 329 |
| 318 * July 16th 2017: Reformatted as a gist. | 330 * July 16th 2017: Reformatted as a gist. |
| 319 | 331 |
| 320 * June 13th 2017: Compile-time error for using a void value was changed to | 332 * June 13th 2017: Compile-time error for using a void value was changed to |
| 321 static warning. | 333 static warning. |
| 322 * June 12th 2017: Grammar changed extensively, to use | 334 |
| 323 `typeNotVoid` rather than | 335 * June 12th 2017: Grammar changed extensively, to use `typeNotVoid` |
| 324 `voidOrType`. | 336 rather than `voidOrType`. |
| 325 * June 5th 2017: Added `typeCast` and | 337 |
| 326 `typeTest` to the locations where void | 338 * June 5th 2017: Added `typeCast` and `typeTest` to the locations where |
| 327 expressions may occur. | 339 void expressions may occur. |
| OLD | NEW |