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 |