OLD | NEW |
---|---|
(Empty) | |
1 ## Feature: Generalized Void | |
2 | |
3 Author: eernst@ | |
4 | |
5 **Status**: Under implementation. | |
6 | |
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 | |
9 generalized support for `void`, without the function type subtype exception | |
10 that this feature includes for backward compatibility in Dart 1.x. This | |
11 document specifies the feature for Dart 1.x and indicates how Dart 2 | |
12 differs at relevant points. | |
13 | |
14 **The feature** described here, *generalized void*, allows for using the | |
15 type `void` as a type annotation, and as a type argument. | |
Lasse Reichstein Nielsen
2017/08/09 13:41:42
Not sure there should be a comma here.
eernst
2017/08/09 14:30:03
Done.
| |
16 | |
17 The **motivation** for allowing the extended usage is that it helps | |
18 developers state the intent that a particular **value should be | |
19 ignored**. For example, a `Future<void>` may be awaited in order to satisfy | |
20 ordering dependencies in the execution, but no useful value will be | |
21 available at completion. Similarly, a `Visitor<void>` (where we assume the | |
22 type argument is used to describe a value returned by the visitor) may be | |
23 used to indicate that the visit is performed for its side-effects | |
24 alone. The generalized void feature includes mechanisms to help developers | |
25 avoid using such a value. | |
Lasse Reichstein Nielsen
2017/08/09 13:41:42
Is it worth mentioning that the type parameter of
eernst
2017/08/09 14:30:04
Added a small paragraph mentioning this, and using
| |
26 | |
27 Note that is not technically dangerous to use a value of type `void`, it | |
28 does not violate any constraints at the level of the language semantics. | |
29 Developers just made the decision to declare that the value is useless, | |
30 based on the program logic. Hence, there is **no requirement** for the | |
31 generalized void mechanism to be strict and **sound**. However, it is the | |
32 intention that the mechanism should be sufficiently strict to make the | |
33 mechanism helpful and non-frustrating in practice. | |
34 | |
35 No constraints are imposed on which values may be given type `void`, so in | |
36 that sense `void` can be considered to be just another name for the type | |
37 `Object`, flagged as useless. Note that this is an approximate rule (in | |
38 Dart 1.x), it fails to hold for function types. | |
39 | |
40 The mechanisms helping developers to avoid using values of type `void` are | |
41 divided into **two phases**. This document specifies the first phase. The | |
42 second phase may be added to Dart 1.x later, or it may only be provided in | |
Lasse Reichstein Nielsen
2017/08/09 13:41:42
Definitely not going into Dart 1.0, everything we
eernst
2017/08/09 14:30:03
Removed the entire sentence. Everything will be ab
| |
43 Dart 2.0, depending on the schedule. | |
44 | |
45 The **first phase** uses restrictions which are based on syntactic criteria | |
46 in order to ensure that direct usage of a value of type `void` is a static | |
47 warning (in Dart 2: an error). A few exceptions are allowed, e.g., type | |
48 casts, such that developers can explicitly make the choice to use such a | |
49 value. The general rule is that all values of type `void` must be | |
50 discarded. | |
51 | |
52 The **second phase** will deal with casts and preservation of | |
53 voidness. Some casts will cause derived expressions to switch from having | |
54 type `void` to having some other type, and hence those casts introduce the | |
55 possibility that "a void value" will get passed and used. Here is an | |
56 example: | |
57 | |
58 ```dart | |
59 class A<T> { T foo(); } | |
60 A<Object> a = new A<void>(); // Violates voidness preservation. | |
61 var x = a.foo(); // Use a "void value", with static type Object. | |
62 ``` | |
63 | |
64 We plan to introduce a **voidness preservation analysis** (which is similar | |
Lasse Reichstein Nielsen
2017/08/09 13:41:42
plan -> intend
eernst
2017/08/09 14:30:03
Done.
| |
65 to a small type system) to keep track of such situations. As mentioned, the | |
66 second phase is **not specified in this document**, and it is possible that | |
67 voidness preservation will be checked in Dart 2 and strong mode, but that | |
68 it is never added to Dart 1.x. In any case, voidness preservation is a | |
Lasse Reichstein Nielsen
2017/08/09 13:41:42
Again, we are past Dart 1.x now.
eernst
2017/08/09 14:30:03
Deleted the part that mentions language versions:
| |
69 purely static analysis, and there are no plans to introduce dynamic | |
70 checking for it. | |
71 | |
72 ## Syntax | |
73 | |
74 The reserved word `void` remains a reserved word, but it will now be usable | |
75 in additional contexts. Below are the grammar rules affected by this | |
76 change. New grammar rules are marked NEW, other grammar rules are | |
77 modified. Unchanged alternatives in a rule are shown as `...`. The grammar | |
78 rules used as a starting point for this syntax are taken from the language | |
79 specification as of June 2nd, 2017 (git commit 0603b18). | |
Lasse Reichstein Nielsen
2017/08/09 13:41:42
We will need to update this for Function types (wh
eernst
2017/08/09 14:30:03
The reference to the spec grammar below (CL 268890
| |
80 | |
81 ``` | |
82 typeNotVoid ::= // NEW | |
83 typeName typeArguments? | |
84 type ::= // ENTIRE RULE MODIFIED | |
85 typeNotVoid | 'void' | |
86 redirectingFactoryConstructorSignature ::= | |
87 'const'? 'factory' identifier ('.' identifier)? | |
88 formalParameterList `=' typeNotVoid ('.' identifier)? | |
89 superclass ::= | |
90 'extends' typeNotVoid | |
91 mixinApplication ::= | |
92 typeNotVoid mixins interfaces? | |
93 typeParameter ::= | |
94 metadata identifier ('extends' typeNotVoid)? | |
95 newExpression ::= | |
96 'new' typeNotVoid ('.' identifier)? arguments | |
97 constObjectExpression ::= | |
98 'const' typeNotVoid ('.' identifier)? arguments | |
99 typeTest ::= | |
100 isOperator typeNotVoid | |
101 typeCast ::= | |
102 asOperator typeNotVoid | |
103 onPart ::= | |
104 catchPart block | | |
105 'on' typeNotVoid catchPart? block | |
106 typeNotVoidList ::= | |
107 typeNotVoid (',' typeNotVoid)* | |
108 mixins ::= | |
109 'with' typeNotVoidList | |
110 interfaces ::= | |
111 'implements' typeNotVoidList | |
112 functionSignature ::= | |
113 metadata type? identifier formalParameterList | |
114 functionFormalParameter ::= | |
115 metadata 'covariant'? type? identifier formalParameterList | |
116 operatorSignature ::= | |
117 type? 'operator' operator formalParameterList | |
118 getterSignature ::= | |
119 type? 'get' identifier | |
120 setterSignature ::= | |
121 type? 'set' identifier formalParameterList | |
122 topLevelDefinition ::= | |
123 ... | |
124 type? 'get' identifier functionBody | | |
125 type? 'set' identifier formalParameterList functionBody | | |
126 ... | |
127 functionPrefix ::= | |
128 type? identifier | |
129 ``` | |
130 | |
131 The rule for `returnType` in the grammar is deleted. | |
132 | |
133 *This is because we can now use `type`, which derives the same expressions | |
134 as `returnType` used to derive. In that sense, some of these grammar | |
135 modifications are renames. Note that the grammar contains known mistakes, | |
136 especially concerned with the placement of `metadata`. This document makes | |
137 no attempt to correct those mistakes, that is a separate issue.* | |
138 | |
139 *A complete grammar which includes support for generalized void is | |
140 available in the file Dart.g | |
141 from | |
142 [https://codereview.chromium.org/2688903004/](https://codereview.chromium.org/26 88903004/).* | |
143 | |
144 ## Dynamic semantics | |
145 | |
146 There are no values at run time whose dynamic type is the type void. | |
147 | |
148 *This implies that it is never required for the getter `runtimeType` in the | |
149 built-in class `Object` to return a reified representation of the type | |
150 void. Note, however, that apart from the fact that usage is restricted for | |
151 values with the type void, it is possible for an expression of type void to | |
152 evaluate to any value. In that sense, every value has the type void, it is | |
153 just not the only type that it has, and loosely speaking it is not the most | |
154 specific type.* | |
155 | |
156 There is no value which is the reified representation of the type void at | |
157 run time. | |
158 | |
159 *Syntactically, `void` cannot occur as an expression, and hence expression | |
160 evaluation cannot directly yield such a value. However, a formal type | |
161 parameter can be used in expressions, and the actual type argument bound to | |
162 that formal type parameter can be the type void. That case is specified | |
163 explicitly below. Apart from the reserved word `void` and a formal type | |
164 parameter, no other term can denote the type void.* | |
165 | |
166 *Conversely, `void` cannot denote any other entity than the type void: | |
167 `void` cannot occur as the declared name of any declaration (including | |
168 library prefixes, types, variables, parameters, etc.). This implies that | |
169 `void` is not subject to scoped lookup, and the name is not exported by any | |
170 system library. Similarly, it can never be accessed using a prefixed | |
171 expression (`p.void`). Hence, `void` has a fixed meaning everywhere in all | |
172 Dart programs, and it can only occur as a stand-alone word.* | |
173 | |
174 When `void` is passed as an actual type argument to a generic class or a | |
175 generic function, and when the type void occurs as a parameter type in a | |
176 function type, the reified representation is equal (according to `==`) to | |
177 the reified representation of the built-in class `Object`. | |
178 | |
179 *It is encouraged for an implementation to use a reified representation for | |
180 `void` as a type argument and as a parameter type in a function type which | |
181 is not `identical` to the reified representation of the built-in class | |
182 `Object`, but they must be equal. This allows implementations to produce | |
183 better diagnostic messages, e.g., in case of a runtime error.* | |
184 | |
185 *This treatment of the reified representation of the type void reinforces | |
186 the understanding that "voidness" is merely a statically known flag on the | |
187 built-in class `Object`, it is not a separate type. However, for backward | |
188 compatibility we need to treat return types differently.* | |
189 | |
190 When `void` is specified as the return type of a function, the reified | |
191 representation of the return type is left unspecified. | |
192 | |
193 *There is no way for a Dart program at run time to obtain a reified | |
194 representation of that return type alone, even when the function type as a | |
195 whole may be obtained (e.g., the function type could be evaluated as an | |
196 expression). It is therefore not necessary to reified representation of | |
197 such a return type.* | |
198 | |
199 *It may be possible to use a reflective subsystem (mirrors) to deconstruct | |
200 a function type whose return type is the type void, but the existing design | |
201 of the system library `dart:mirrors` already handles this case by allowing | |
202 for a type mirror that does not have a reflected type.* | |
203 | |
204 Consider a type _T_ where the type void occurs as an actual type argument | |
205 to a generic class, or as a parameter type in a function type. Dynamically, | |
206 the more-specific-than relation (`<<`) and the dynamic subtype relation | |
207 (`<:`) between _T_ and other types are determined by the following rule: | |
208 the type void is treated as being the built-in class `Object`. | |
209 | |
210 *Dart 1.x does not support generic function types dynamically, because they | |
211 are erased to regular function types during compilation. Hence there is no | |
212 need to specify the the typing relations for generic function types. In | |
213 Dart 2, the subtype relationship for generic function types follows from | |
214 the rule that `void` is treated as `Object`.* | |
215 | |
216 Consider a function type _T_ where the return type is the type void. The | |
217 dynamic more-specific-than relation, `<<`, and the dynamic subtype | |
218 relation, `<:`, are determined by the existing rules in the language | |
219 specification, supplemented by the above rule for handling occurrences of | |
220 the type void other than as a return type. | |
221 | |
222 *This ensures backward compatibility for the cases where the type void can | |
223 be used already today. It follows that it will be a breaking change to | |
224 switch to a ruleset where the type void even as a return type is treated | |
225 like the built-in class Object, i.e. when switching to Dart 2.0. However, | |
226 the only situation where the semantics differs is as follows: Consider a | |
227 situation where a value of type `void Function(...)` is assigned to a | |
228 variable or parameter `x` whose type annotation is `Object Function(...)`, | |
229 where the argument types are arbitrary, but such that the assignment is | |
230 permitted. In that situation, in checked mode, the assignment will fail | |
231 with the current semantics, because the type of that value is not a subtype | |
232 of the type of `x`. The rules in this document preserve that behavior. If | |
233 we were to consistently treat the type void as `Object` at run time (as in | |
234 Dart 2) then this assignment would be permitted (but we would then use | |
235 voidness preservation to detect and avoid this situation at compile time).* | |
236 | |
237 *The semantics of checked mode checks involving types where the type void | |
238 occurs is determined by the semantics of subtype tests, so we do not | |
239 specify that separately.* | |
240 | |
241 An instantiation of a generic class `G` is malbounded if it contains `void` | |
242 as an actual type argument for a formal type parameter, unless that type | |
243 parameter does not have a bound, or it has a bound which is the built-in | |
244 class `Object`, or `dynamic`. | |
245 | |
246 *The treatment of malbounded types follows the current specification.* | |
247 | |
248 ## Static Analysis | |
249 | |
250 For the static analysis, the more-specific-than relation, `<<`, and the | |
251 subtype relation, `<:`, are determined by the same rules as described above | |
252 for the dynamic semantics. | |
253 | |
254 *That is, the type void is considered to be equivalent to the built-in | |
255 class `Object`, except when used as a return type, in which case it is | |
256 effectively considered to be a proper supertype of `Object`. As mentioned, | |
257 voidness preservation is a separate analysis which is not specified by this | |
258 document, but it is intended to be used in the future to track "voidness" | |
259 in types and flag implicit casts wherein information about voidness may | |
260 indirectly be lost. With voidness preservation in place, we expect to be | |
261 able to treat the type void as `Object` in all cases during subtype | |
262 checks.* | |
263 | |
264 It is a static warning for an expression to have type void, except for the | |
Lasse Reichstein Nielsen
2017/08/09 13:41:42
In Dart 2, this will be a compile-time error, so j
eernst
2017/08/09 14:30:03
Ah, that's funny: I had 'compile-time error' here
| |
265 following situations: | |
266 | |
267 * In an expressionStatement `e;`, e may have type void. | |
268 * In the initialization and increment expressions of a for-loop, | |
269 `for (e1; e2; e3) {..}`, `e1` and `e3` may have type void. | |
270 * In a typeCast `e as T`, `e` may have type void. | |
271 * In a typeTest `e is T` or `e is! T`, `e` may have type void. | |
272 * In a parenthesized expression `(e)`, `e` may have type void. | |
273 * In a return statement `return e;`, when the return type of the innermost | |
274 enclosing function is the type void, `e` may have type void. | |
275 | |
276 *Note that the parenthesized expression itself has type void, so it is | |
277 again subject to the same constraints. Also note that we may not allow | |
278 return statements returning an expression of type void in the future, but | |
279 it is allowed here for backward compatibility.* | |
280 | |
281 During bounds checking, it is possible that a bound of a formal type | |
282 parameter of a generic class or function is statically known to be the type | |
283 void. In this case, the bound is considered to be the built-in class | |
284 `Object`. | |
285 | |
286 ## Discussion | |
287 | |
288 Expressions derived from typeCast and typeTest do not support `void` as the | |
289 target type. We have omitted support for this situation because we consider | |
290 it to be useless. If void is passed indirectly via a type variable `T` then | |
291 `e as T`, `e is T`, and `e is! T` will treat `T` like `Object`. In general, | |
292 the rationale is that the type void admits all values (because it is just | |
293 `Object` plus a "static voidness flag"), but values of type void should be | |
294 discarded. | |
295 | |
296 The treatment of bounds is delicate. We syntactically prohibit `void` as a | |
297 bound of a formal type parameter of a generic class or function. It is | |
298 possible to pass the type void as an actual type argument to a generic | |
299 class, and that type argument might in turn be used as the bound of another | |
300 formal type parameter of the class, or of a generic function in the | |
301 class. It would be possible to make it a compile-time error to pass `void` | |
302 as a type argument to a generic class where it will be used as a bound, but | |
303 this would presumably require a transitive traversal of all generic classes | |
304 and functions where the corresponding formal type parameter is passed on to | |
305 other generic classes or functions, which would be highly brittle: A tiny | |
306 change to a generic class or function could break code far away. So we do | |
307 not wish to prevent formal type parameter bounds from indirectly becoming | |
308 the type void. This motivated the decision to treat such a void-valued | |
309 bound as `Object`. | |
310 | |
311 ## Updates | |
312 | |
313 * August 9th 2017: Transferred to SDK repo, docs/language/informal. | |
314 | |
315 * July 16th 2017: Reformatted as a gist. | |
316 | |
317 * June 13th 2017: Compile-time error for using a void value was changed to | |
318 static warning. | |
319 * June 12th 2017: Grammar changed extensively, to use | |
320 `typeNotVoid` rather than | |
321 `voidOrType`. | |
322 * June 5th 2017: Added `typeCast` and | |
323 `typeTest` to the locations where void | |
324 expressions may occur. | |
OLD | NEW |