|
| 1 | +# Implicit Creation |
| 2 | + |
| 3 | +Author: eernst@. |
| 4 | + |
| 5 | +Version: 0.5 (2018-01-04) |
| 6 | + |
| 7 | +Status: Under implementation. |
| 8 | + |
| 9 | +**This document** is an informal specification of the *implicit creation* feature. |
| 10 | +**The feature** adds support for omitting some occurrences of the reserved words |
| 11 | +`new` and `const` in instance creation expressions. |
| 12 | + |
| 13 | +This feature specification was written with a |
| 14 | +[combined proposal](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-new-const.md) |
| 15 | +as the starting point. That proposal presents optional new and optional const |
| 16 | +together with several other features. |
| 17 | + |
| 18 | + |
| 19 | +## Motivation |
| 20 | + |
| 21 | +In Dart without implicit creation, the reserved word `new` is present in |
| 22 | +almost all expressions whose evaluation invokes a constructor at run time, |
| 23 | +and `const` is present in the corresponding constant expressions. These |
| 24 | +expressions are known as *instance creation expressions*. If `new` or |
| 25 | +`const` is removed from such an instance creation expression, the remaining |
| 26 | +phrase is still syntactically correct in most cases. This feature |
| 27 | +specification updates the grammar to make them all syntactically correct. |
| 28 | + |
| 29 | +With that grammar update, all instance creation expressions can technically |
| 30 | +omit `new` or `const` because tools (compilers, analyzers) are able to |
| 31 | +parse these expressions. The tools are able to recognize that these |
| 32 | +expressions denote instance creations (rather than, say, static function |
| 33 | +invocations), because the part before the arguments is statically known to |
| 34 | +denote a constructor. |
| 35 | + |
| 36 | +For instance, `p.C.foo` may resolve statically to a constructor named `foo` in |
| 37 | +a class `C` imported with prefix `p`. Similarly, `D` may resolve to a class, in |
| 38 | +which case `D(42)` is statically known to be a constructor invocation because |
| 39 | +the other interpretation is statically known to be incorrect (that is, cf. |
| 40 | +section '16.14.3 Unqualified Invocation' in the language specification, |
| 41 | +evaluating `(D)(42)`: `(D)` is an instance of `Type` which is not a function |
| 42 | +type and does not have a method named `call`, so we cannot call `(D)`). |
| 43 | + |
| 44 | +In short, even without the keyword, we can still unambiguously recognize the |
| 45 | +expressions that create objects. In that sense, the keywords are superfluous. |
| 46 | + |
| 47 | +For human readers, however, it may be helpful to document that a particular |
| 48 | +expression will yield a fresh instance, and this is the most common argument why |
| 49 | +`new` should *not* be omitted: It can be good documentation. But Dart already |
| 50 | +allows instance creation expressions to invoke a factory constructor, which is |
| 51 | +not guaranteed to return a newly created object, so Dart developers never had |
| 52 | +any firm local guarantees that any particular expression would yield a fresh |
| 53 | +object. This means that it may very well be justified to have an explicit `new`, |
| 54 | +but it will never be a rigorous guarantee of freshness. |
| 55 | + |
| 56 | +Similarly, it may be important for developers to ensure that certain expressions |
| 57 | +are constant, because of the improved performance and the guaranteed |
| 58 | +canonicalization. This is a compelling argument in favor of making certain |
| 59 | +instance creation expressions constant: It is simply a bug for that same |
| 60 | +expression to have `new` because object identity is an observable |
| 61 | +characteristic, and it may be crucial for performance that the expression is |
| 62 | +constant. |
| 63 | + |
| 64 | +In summary, both `new` and `const` may always be omitted from an instance |
| 65 | +creation expression, but it is useful and reasonable to allow an explicit `new`, |
| 66 | +and it is necessary to allow an explicit `const`. Based on that line of |
| 67 | +reasoning, we've decided to make them optional. It will then be possible for |
| 68 | +developers to make many expressions considerably more concise, and they can |
| 69 | +still enforce the desired semantics as needed. |
| 70 | + |
| 71 | +Obviously, this underscores the importance of the default: When a given instance |
| 72 | +creation expression omits the keyword, should it be `const` or `new`? |
| 73 | + |
| 74 | +**For instance creation expressions we have chosen** to use `const` whenever |
| 75 | +possible, and otherwise `new`. |
| 76 | + |
| 77 | +This implies that `const` is the preferred choice for instance creation. There |
| 78 | +is a danger that `const` is chosen by default in some cases where this is not |
| 79 | +intended by the developer, and the affected software will have bugs which are |
| 80 | +hard to spot. In particular, `e1 == e2` may evaluate to true in cases where it |
| 81 | +would have yielded false with `new` objects. |
| 82 | + |
| 83 | +We consider that danger to be rather small, because `const` can only be chosen |
| 84 | +in cases where the denoted constructor is constant, and with a class with a |
| 85 | +constant constructor it is necessary for developers to treat all accesses to its |
| 86 | +instances in such a way that the software will still work correctly even when |
| 87 | +any given instance was obtained by evaluation of a constant expression. The |
| 88 | +point is that, for such a class, we can never know for sure that any given |
| 89 | +instance is _not_ a constant object. |
| 90 | + |
| 91 | +With composite literals such as lists and maps, a `const` modifier may be |
| 92 | +included in order to make it a constant expression (which will of course fail if |
| 93 | +it contains something which is not a constant expression). In this case the |
| 94 | +presence of `const` may again be crucial, for the same reasons as with an |
| 95 | +instance creation expression, but it may also be crucial that `const` is _not_ |
| 96 | +present, because the list or map will be mutated. |
| 97 | + |
| 98 | +**For composite literals we have chosen** to implicitly introduce `const` |
| 99 | +whenever it is required by the context. |
| 100 | + |
| 101 | +The choice to include `const` only when required by context (rather than |
| 102 | +whenever possible) is strictly less aggressive than the approach with instance |
| 103 | +creations. This choice is necessary because there is no way for developers to |
| 104 | +ensure that a literal like `[1, 2]` is mutable, if permitted by the context, |
| 105 | +other than omitting `const`. Furthermore, we expect this choice to be |
| 106 | +convenient in practice, because mutable data structures are used frequently. So |
| 107 | +developers must expect to write an explicit `const` on composite literals now |
| 108 | +and then. |
| 109 | + |
| 110 | +In summary, the implicit creation feature allows for concise construction of |
| 111 | +objects, with a slight preference for constant expressions, and it still allows |
| 112 | +developers to explicitly specify `new` or `const`, whenever needed and whenever |
| 113 | +it is considered to be good documentation. |
| 114 | + |
| 115 | + |
| 116 | +## Syntax |
| 117 | + |
| 118 | +The syntax changes associated with this feature are the following: |
| 119 | + |
| 120 | +``` |
| 121 | +postfixExpression ::= |
| 122 | + assignableExpression postfixOperator | |
| 123 | + constructorInvocation selector* | // NEW |
| 124 | + primary selector* |
| 125 | +constructorInvocation ::= // NEW |
| 126 | + typeName typeArguments '.' identifier arguments |
| 127 | +assignableExpression ::= |
| 128 | + SUPER unconditionalAssignableSelector | |
| 129 | + constructorInvocation assignableSelectorPart+ | // NEW |
| 130 | + identifier | |
| 131 | + primary assignableSelectorPart+ |
| 132 | +assignableSelectorPart ::= |
| 133 | + argumentPart* assignableSelector |
| 134 | +``` |
| 135 | + |
| 136 | + |
| 137 | +## Static analysis |
| 138 | + |
| 139 | +We specify a type directed source code transformation which eliminates the |
| 140 | +feature by expressing the same semantics with different syntax. The static |
| 141 | +analysis proceeds to work on the transformed program. |
| 142 | + |
| 143 | +*This means that the feature is "static semantic sugar". We do not specify the |
| 144 | +dynamic semantics for this feature, because the feature is eliminated in this |
| 145 | +transformation step.* |
| 146 | + |
| 147 | +We need to treat expressions differently in different locations, hence the |
| 148 | +following definition: An expression _e_ is said to *occur in a constant |
| 149 | +context*, |
| 150 | + |
| 151 | +- if _e_ is an element of a constant list literal, or a key or value of |
| 152 | + an entry of a constant map literal. |
| 153 | +- if _e_ is an actual argument of a constant object expression or of a |
| 154 | + metadata annotation. |
| 155 | +- if _e_ is the initializing expression of a constant variable declaration. |
| 156 | +- if _e_ is a switch case expression. |
| 157 | +- if _e_ is an immediate subexpression of an expression _e1_ which occurs in |
| 158 | + a constant context, unless _e1_ is a `throw` expression or a function |
| 159 | + literal. |
| 160 | + |
| 161 | +*This roughly means that everything which is inside a syntactically |
| 162 | +constant expression is in a constant context. Note that a `const` modifier |
| 163 | +which is introduced by the source code transformation does not create a |
| 164 | +constant context, it is only the explicit occurrences of `const` in the |
| 165 | +program that create a constant context. Also note that a `throw` expression |
| 166 | +is currently not allowed in a constant expression, but extensions affecting |
| 167 | +that status may be considered. A similar situation arises for function |
| 168 | +literals.* |
| 169 | + |
| 170 | +The transformation consists of two steps. In the first step, every literal |
| 171 | +list and literal map _e_ which occurs in a constant context and does not |
| 172 | +have the modifier `const` is replaced by `const` _e_. |
| 173 | + |
| 174 | +We define *new/const insertion* as the following transformation, which will |
| 175 | +be applied to specific parts of the program as specified below: |
| 176 | + |
| 177 | +- if the expression _e_ occurs in a constant context, replace _e_ by |
| 178 | + `const` _e_, |
| 179 | +- if the expression _e_ does not occur in a constant context, but `const` |
| 180 | + _e_ is a correct constant expression, replace _e_ by `const` _e_, |
| 181 | +- otherwise replace _e_ by `new` _e_. |
| 182 | + |
| 183 | +*Note that this transformation is applied in a bottom-up order which implies |
| 184 | +that all relevant transformations have already been applied on subexpressions |
| 185 | +of _e_. Also note that this transformation is only applied to syntactic |
| 186 | +constructs where the outcome is a syntactically correct instance creation |
| 187 | +expression. On the other hand, the outcome may have static semantic errors, |
| 188 | +e.g., actual arguments to a constructor invocation may have wrong types |
| 189 | +because that's how the program was written.* |
| 190 | + |
| 191 | +We define *new insertion* as the following transformation, which will be |
| 192 | +applied as specified below: |
| 193 | + |
| 194 | +- replace _e_ by `new` _e_. |
| 195 | + |
| 196 | +*We specify the second step of the transformation as based on a depth-first |
| 197 | +traversal of an abstract syntax tree (AST). This means that the program is |
| 198 | +assumed to be free of syntax errors, and when the current AST is, e.g., a |
| 199 | +`postfixExpression`, the program as a whole has such a structure that the |
| 200 | +current location was parsed as a `postfixExpression`. This is different |
| 201 | +from the situation where we just require that a given subsequence of the |
| 202 | +tokens of the program allows for such a parsing in isolation. For instance, |
| 203 | +an identifier like `x` parses as an `assignableExpression` in isolation, |
| 204 | +but if it occurs in the context `var x = 42;` or `var y = x;` then it will |
| 205 | +not be parsed as an `assignableExpression`, it will be parsed as a plain |
| 206 | +`identifier` which is part of a `declaredIdentifier` in the first case, and |
| 207 | +as a `primary` which is a `postfixExpression`, which is a |
| 208 | +`unaryExpression`, etc., in the second case. In short, we are transforming |
| 209 | +the AST of the program as a whole, not isolated snippets of code.* |
| 210 | + |
| 211 | +*In scientific literature, this kind of transformation is commonly |
| 212 | +specified as an inductive transformation where `[[e1 e2]] = [[e1]] [[e2]]` |
| 213 | +when the language supports a construct of the form `e1 e2`, etc. The reader |
| 214 | +may prefer to view the transformation in that light, and we would then say |
| 215 | +that we have omitted all the congruence rules.* |
| 216 | + |
| 217 | +An expression of one of the following forms must be modified in bottom-up |
| 218 | +order to be or contain a `constantObjectExpression` or `newExpression` |
| 219 | +as described: |
| 220 | + |
| 221 | +With a `postfixExpression` _e_, |
| 222 | + |
| 223 | +- if _e_ is of the form `constructorInvocation selector*`, i.e., |
| 224 | + `typeName typeArguments '.' identifier arguments selector*` then perform |
| 225 | + new/const insertion on the initial `constructorInvocation`. |
| 226 | +- if _e_ is of the form |
| 227 | + `typeIdentifier arguments` where `typeIdentifier` denotes a class then |
| 228 | + perform new/const insertion on _e_. |
| 229 | +- if _e_ is of the form |
| 230 | + `identifier1 '.' identifier2 arguments` where `identifier1` denotes |
| 231 | + a class and `identifier2` is the name of a named constructor in that class, |
| 232 | + or `identifier1` denotes a prefix for a library _L_ and `identifier2` denotes |
| 233 | + a class exported by _L_, perform new/const insertion on _e_. |
| 234 | +- if _e_ is of the form |
| 235 | + `identifier1 '.' typeIdentifier '.' identifier2 arguments` where |
| 236 | + `identifier1` denotes a library prefix for a library _L_, `typeIdentifier` |
| 237 | + denotes a class _C_ exported by _L_, and `identifier2` is the name of a named |
| 238 | + constructor in _C_, perform new/const insertion on _e_. |
| 239 | + |
| 240 | +For the purposes of describing the transformation on assignable expressions |
| 241 | +we need the following syntactic entity: |
| 242 | + |
| 243 | +``` |
| 244 | +assignableExpressionTail ::= |
| 245 | + arguments assignableSelector assignableSelectorPart* |
| 246 | +``` |
| 247 | + |
| 248 | +With an `assignableExpression` _e_, |
| 249 | + |
| 250 | +- if _e_ is of the form |
| 251 | + `constructorInvocation assignableSelectorPart+` |
| 252 | + then perform new/const insertion on the initial |
| 253 | + `constructorInvocation`. |
| 254 | +- if _e_ is of the form |
| 255 | + `typeIdentifier assignableExpressionTail` |
| 256 | + where `typeIdentifier` denotes a class then perform new/const insertion on |
| 257 | + the initial `typeIdentifier arguments`. |
| 258 | +- if _e_ is of the form |
| 259 | + `typeIdentifier '.' identifier assignableExpressionTail` |
| 260 | + where `typeIdentifier` denotes a class and `identifier` is the name of |
| 261 | + a named constructor in that class, or `typeIdentifier` denotes a prefix |
| 262 | + for a library _L_ and `identifier` denotes a class exported by _L_ |
| 263 | + then perform new/const insertion on the initial |
| 264 | + `typeIdentifier '.' identifier arguments`. |
| 265 | +- if _e_ is of the form |
| 266 | + `typeIdentifier1 '.' typeIdentifier2 '.' identifier assignableExpressionTail` |
| 267 | + Where `typeIdentifier1` denotes a library prefix for a library _L_, |
| 268 | + `typeIdentifier2` denotes a class _C_ exported by _L_, and `identifier` |
| 269 | + is the name of a named constructor in _C_ then perform new/const insertion |
| 270 | + on the initial |
| 271 | + `typeIdentifier1 '.' typeIdentifier2 '.' identifier arguments`. |
| 272 | + |
| 273 | +*In short, add `const` wherever possible on terms that invoke a |
| 274 | +constructor, and otherwise add `new`. It is easy to verify that each of the |
| 275 | +replacements can be derived from `postfixExpression` via `primary |
| 276 | +selector*` and similarly for `assignableExpression`. Hence, the |
| 277 | +transformation preserves syntactic correctness.* |
| 278 | + |
| 279 | + |
| 280 | +## Dynamic Semantics |
| 281 | + |
| 282 | +There is no dynamic semantics to specify for this feature because it is |
| 283 | +eliminated by code transformation. |
| 284 | + |
| 285 | + |
| 286 | +## Revisions |
| 287 | + |
| 288 | +- 0.5 (2018-01-04) Rewritten to use `const` whenever possible (aka "magic |
| 289 | + const") and adjusted to specify optional const as well as optional new |
| 290 | + together, because they are now very closely connected. This document was |
| 291 | + renamed to 'implicit-creation.md', and the document 'optional-const.md' |
| 292 | + was deleted. |
| 293 | + |
| 294 | +- 0.4 (2017-10-17) Reverted to use 'immediate subexpression' again, for |
| 295 | + correctness. Adjusted terminology for consistency. Clarified the semantics |
| 296 | + of the transformation. |
| 297 | + |
| 298 | +- 0.3 (2017-09-08) Included missing rule for transformation of composite |
| 299 | + literals (lists and maps). Eliminated the notion of an immediate |
| 300 | + subexpression, for improved precision. |
| 301 | + |
| 302 | +- 0.2 (2017-07-30) Updated the document to specify the previously missing |
| 303 | + transformations for `assignableExpression`, and to specify a no-magic |
| 304 | + approach (where no `const` is introduced except when forced by the |
| 305 | + syntactic context). |
| 306 | + |
| 307 | +- 0.1 (2017-08-15) Stand-alone informal specification for optional new created, |
| 308 | + using version 0.8 of the combined proposal |
| 309 | + [optional-new-const.md](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-new-const.md) |
| 310 | + as the starting point. |
0 commit comments