Chromium Code Reviews| 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 |