Skip to content

Commit 6e3a274

Browse files
committed
"Magic" implicit creation.
Description which is relevant as of January 2018: The more aggressive approach where `const` is inserted whenever possible (for instance creation expressions) and whenever required (for composite literals), and `new` is inserted otherwise, is now the agreed policy. This means that the remaining discussion is on the specification of this policy, not the policy itself. This has been true approximately since Dec 1st, 2017. Here is a brief overview of the feature which is specified in the document added by this CL: The introduction of 'implicit creation' adjusts the grammar such that instance creation expressions (such as `new C()`) can always omit the keyword (`new` or `const`). The semantics is that these expressions will still invoke the same constructor (which may produce a fresh instance in case of a generative constructor, and some object that may or may not be new in case of a factory), as if `new` or `const` had been added implicitly. The choice of whether to add `new` or `const` is made according to the policy described above. ---------------------------------------------------------------------- Original description, FYI: Copy of Rietveld 3012703002: magic optional const Adjusted informal spec optional-new to insert `const` whenever possible The current version of optional-new.md specifies that `const` should be inserted into certain syntactic phrases (when specific identifiers satisfy some constraints), but only when these phrases occur in a const context. This CL changes that approach and specifies that `const` should also be inserted outside const contexts, if the resulting expression is a correct constant expression. We haven't yet decided on whether we want the current (predictable) model or we want the more "magic" one specified in this CL; this CL is intended to be landed if we choose the latter, and discarded if we choose the former. Change-Id: I602e7eaf3d4c7904277af45c6f62089c77bd5117 Reviewed-on: https://dart-review.googlesource.com/3160 Reviewed-by: Leaf Petersen <[email protected]> Reviewed-by: Lasse R.H. Nielsen <[email protected]>
1 parent 4c402f0 commit 6e3a274

File tree

4 files changed

+323
-438
lines changed

4 files changed

+323
-438
lines changed

docs/language/Dart.g

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -802,9 +802,15 @@ awaitExpression
802802
postfixExpression
803803
: (assignableExpression postfixOperator) =>
804804
assignableExpression postfixOperator
805+
| (typeName typeArguments '.') =>
806+
constructorInvocation ((selector) => selector)*
805807
| primary ((selector) => selector)*
806808
;
807809
810+
constructorInvocation
811+
: typeName typeArguments '.' identifier arguments
812+
;
813+
808814
postfixOperator
809815
: incrementOperator
810816
;
@@ -834,8 +840,11 @@ assignableExpression
834840
: (SUPER unconditionalAssignableSelector
835841
~('<' | '(' | '[' | '.' | '?.')) =>
836842
SUPER unconditionalAssignableSelector
843+
| (typeName typeArguments '.' identifier '(') =>
844+
constructorInvocation
845+
((assignableSelectorPart) => assignableSelectorPart)+
837846
| (identifier ~('<' | '(' | '[' | '.' | '?.')) => identifier
838-
| (primary argumentPart* assignableSelector) =>
847+
| (primary assignableSelectorPart) =>
839848
primary ((assignableSelectorPart) => assignableSelectorPart)+
840849
| identifier
841850
;
@@ -947,7 +956,8 @@ nonLabelledStatement
947956
| breakStatement
948957
| continueStatement
949958
| returnStatement
950-
| (functionSignature functionBodyPrefix) => localFunctionDeclaration
959+
| (metadata functionSignature functionBodyPrefix) =>
960+
localFunctionDeclaration
951961
| assertStatement
952962
| (YIELD ~'*') => yieldStatement
953963
| yieldEachStatement
@@ -967,7 +977,7 @@ initializedVariableDeclaration
967977
;
968978
969979
localFunctionDeclaration
970-
: functionSignature functionBody
980+
: metadata functionSignature functionBody
971981
;
972982
973983
ifStatement
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
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

Comments
 (0)