mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:41:41 +00:00
"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 <leafp@google.com> Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
This commit is contained in:
parent
4c402f06da
commit
6e3a274c5f
|
@ -802,9 +802,15 @@ awaitExpression
|
|||
postfixExpression
|
||||
: (assignableExpression postfixOperator) =>
|
||||
assignableExpression postfixOperator
|
||||
| (typeName typeArguments '.') =>
|
||||
constructorInvocation ((selector) => selector)*
|
||||
| primary ((selector) => selector)*
|
||||
;
|
||||
|
||||
constructorInvocation
|
||||
: typeName typeArguments '.' identifier arguments
|
||||
;
|
||||
|
||||
postfixOperator
|
||||
: incrementOperator
|
||||
;
|
||||
|
@ -834,8 +840,11 @@ assignableExpression
|
|||
: (SUPER unconditionalAssignableSelector
|
||||
~('<' | '(' | '[' | '.' | '?.')) =>
|
||||
SUPER unconditionalAssignableSelector
|
||||
| (typeName typeArguments '.' identifier '(') =>
|
||||
constructorInvocation
|
||||
((assignableSelectorPart) => assignableSelectorPart)+
|
||||
| (identifier ~('<' | '(' | '[' | '.' | '?.')) => identifier
|
||||
| (primary argumentPart* assignableSelector) =>
|
||||
| (primary assignableSelectorPart) =>
|
||||
primary ((assignableSelectorPart) => assignableSelectorPart)+
|
||||
| identifier
|
||||
;
|
||||
|
@ -947,7 +956,8 @@ nonLabelledStatement
|
|||
| breakStatement
|
||||
| continueStatement
|
||||
| returnStatement
|
||||
| (functionSignature functionBodyPrefix) => localFunctionDeclaration
|
||||
| (metadata functionSignature functionBodyPrefix) =>
|
||||
localFunctionDeclaration
|
||||
| assertStatement
|
||||
| (YIELD ~'*') => yieldStatement
|
||||
| yieldEachStatement
|
||||
|
@ -967,7 +977,7 @@ initializedVariableDeclaration
|
|||
;
|
||||
|
||||
localFunctionDeclaration
|
||||
: functionSignature functionBody
|
||||
: metadata functionSignature functionBody
|
||||
;
|
||||
|
||||
ifStatement
|
||||
|
|
310
docs/language/informal/implicit-creation.md
Normal file
310
docs/language/informal/implicit-creation.md
Normal file
|
@ -0,0 +1,310 @@
|
|||
# Implicit Creation
|
||||
|
||||
Author: eernst@.
|
||||
|
||||
Version: 0.5 (2018-01-04)
|
||||
|
||||
Status: Under implementation.
|
||||
|
||||
**This document** is an informal specification of the *implicit creation* feature.
|
||||
**The feature** adds support for omitting some occurrences of the reserved words
|
||||
`new` and `const` in instance creation expressions.
|
||||
|
||||
This feature specification was written with a
|
||||
[combined proposal](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-new-const.md)
|
||||
as the starting point. That proposal presents optional new and optional const
|
||||
together with several other features.
|
||||
|
||||
|
||||
## Motivation
|
||||
|
||||
In Dart without implicit creation, the reserved word `new` is present in
|
||||
almost all expressions whose evaluation invokes a constructor at run time,
|
||||
and `const` is present in the corresponding constant expressions. These
|
||||
expressions are known as *instance creation expressions*. If `new` or
|
||||
`const` is removed from such an instance creation expression, the remaining
|
||||
phrase is still syntactically correct in most cases. This feature
|
||||
specification updates the grammar to make them all syntactically correct.
|
||||
|
||||
With that grammar update, all instance creation expressions can technically
|
||||
omit `new` or `const` because tools (compilers, analyzers) are able to
|
||||
parse these expressions. The tools are able to recognize that these
|
||||
expressions denote instance creations (rather than, say, static function
|
||||
invocations), because the part before the arguments is statically known to
|
||||
denote a constructor.
|
||||
|
||||
For instance, `p.C.foo` may resolve statically to a constructor named `foo` in
|
||||
a class `C` imported with prefix `p`. Similarly, `D` may resolve to a class, in
|
||||
which case `D(42)` is statically known to be a constructor invocation because
|
||||
the other interpretation is statically known to be incorrect (that is, cf.
|
||||
section '16.14.3 Unqualified Invocation' in the language specification,
|
||||
evaluating `(D)(42)`: `(D)` is an instance of `Type` which is not a function
|
||||
type and does not have a method named `call`, so we cannot call `(D)`).
|
||||
|
||||
In short, even without the keyword, we can still unambiguously recognize the
|
||||
expressions that create objects. In that sense, the keywords are superfluous.
|
||||
|
||||
For human readers, however, it may be helpful to document that a particular
|
||||
expression will yield a fresh instance, and this is the most common argument why
|
||||
`new` should *not* be omitted: It can be good documentation. But Dart already
|
||||
allows instance creation expressions to invoke a factory constructor, which is
|
||||
not guaranteed to return a newly created object, so Dart developers never had
|
||||
any firm local guarantees that any particular expression would yield a fresh
|
||||
object. This means that it may very well be justified to have an explicit `new`,
|
||||
but it will never be a rigorous guarantee of freshness.
|
||||
|
||||
Similarly, it may be important for developers to ensure that certain expressions
|
||||
are constant, because of the improved performance and the guaranteed
|
||||
canonicalization. This is a compelling argument in favor of making certain
|
||||
instance creation expressions constant: It is simply a bug for that same
|
||||
expression to have `new` because object identity is an observable
|
||||
characteristic, and it may be crucial for performance that the expression is
|
||||
constant.
|
||||
|
||||
In summary, both `new` and `const` may always be omitted from an instance
|
||||
creation expression, but it is useful and reasonable to allow an explicit `new`,
|
||||
and it is necessary to allow an explicit `const`. Based on that line of
|
||||
reasoning, we've decided to make them optional. It will then be possible for
|
||||
developers to make many expressions considerably more concise, and they can
|
||||
still enforce the desired semantics as needed.
|
||||
|
||||
Obviously, this underscores the importance of the default: When a given instance
|
||||
creation expression omits the keyword, should it be `const` or `new`?
|
||||
|
||||
**For instance creation expressions we have chosen** to use `const` whenever
|
||||
possible, and otherwise `new`.
|
||||
|
||||
This implies that `const` is the preferred choice for instance creation. There
|
||||
is a danger that `const` is chosen by default in some cases where this is not
|
||||
intended by the developer, and the affected software will have bugs which are
|
||||
hard to spot. In particular, `e1 == e2` may evaluate to true in cases where it
|
||||
would have yielded false with `new` objects.
|
||||
|
||||
We consider that danger to be rather small, because `const` can only be chosen
|
||||
in cases where the denoted constructor is constant, and with a class with a
|
||||
constant constructor it is necessary for developers to treat all accesses to its
|
||||
instances in such a way that the software will still work correctly even when
|
||||
any given instance was obtained by evaluation of a constant expression. The
|
||||
point is that, for such a class, we can never know for sure that any given
|
||||
instance is _not_ a constant object.
|
||||
|
||||
With composite literals such as lists and maps, a `const` modifier may be
|
||||
included in order to make it a constant expression (which will of course fail if
|
||||
it contains something which is not a constant expression). In this case the
|
||||
presence of `const` may again be crucial, for the same reasons as with an
|
||||
instance creation expression, but it may also be crucial that `const` is _not_
|
||||
present, because the list or map will be mutated.
|
||||
|
||||
**For composite literals we have chosen** to implicitly introduce `const`
|
||||
whenever it is required by the context.
|
||||
|
||||
The choice to include `const` only when required by context (rather than
|
||||
whenever possible) is strictly less aggressive than the approach with instance
|
||||
creations. This choice is necessary because there is no way for developers to
|
||||
ensure that a literal like `[1, 2]` is mutable, if permitted by the context,
|
||||
other than omitting `const`. Furthermore, we expect this choice to be
|
||||
convenient in practice, because mutable data structures are used frequently. So
|
||||
developers must expect to write an explicit `const` on composite literals now
|
||||
and then.
|
||||
|
||||
In summary, the implicit creation feature allows for concise construction of
|
||||
objects, with a slight preference for constant expressions, and it still allows
|
||||
developers to explicitly specify `new` or `const`, whenever needed and whenever
|
||||
it is considered to be good documentation.
|
||||
|
||||
|
||||
## Syntax
|
||||
|
||||
The syntax changes associated with this feature are the following:
|
||||
|
||||
```
|
||||
postfixExpression ::=
|
||||
assignableExpression postfixOperator |
|
||||
constructorInvocation selector* | // NEW
|
||||
primary selector*
|
||||
constructorInvocation ::= // NEW
|
||||
typeName typeArguments '.' identifier arguments
|
||||
assignableExpression ::=
|
||||
SUPER unconditionalAssignableSelector |
|
||||
constructorInvocation assignableSelectorPart+ | // NEW
|
||||
identifier |
|
||||
primary assignableSelectorPart+
|
||||
assignableSelectorPart ::=
|
||||
argumentPart* assignableSelector
|
||||
```
|
||||
|
||||
|
||||
## Static analysis
|
||||
|
||||
We specify a type directed source code transformation which eliminates the
|
||||
feature by expressing the same semantics with different syntax. The static
|
||||
analysis proceeds to work on the transformed program.
|
||||
|
||||
*This means that the feature is "static semantic sugar". We do not specify the
|
||||
dynamic semantics for this feature, because the feature is eliminated in this
|
||||
transformation step.*
|
||||
|
||||
We need to treat expressions differently in different locations, hence the
|
||||
following definition: An expression _e_ is said to *occur in a constant
|
||||
context*,
|
||||
|
||||
- if _e_ is an element of a constant list literal, or a key or value of
|
||||
an entry of a constant map literal.
|
||||
- if _e_ is an actual argument of a constant object expression or of a
|
||||
metadata annotation.
|
||||
- if _e_ is the initializing expression of a constant variable declaration.
|
||||
- if _e_ is a switch case expression.
|
||||
- if _e_ is an immediate subexpression of an expression _e1_ which occurs in
|
||||
a constant context, unless _e1_ is a `throw` expression or a function
|
||||
literal.
|
||||
|
||||
*This roughly means that everything which is inside a syntactically
|
||||
constant expression is in a constant context. Note that a `const` modifier
|
||||
which is introduced by the source code transformation does not create a
|
||||
constant context, it is only the explicit occurrences of `const` in the
|
||||
program that create a constant context. Also note that a `throw` expression
|
||||
is currently not allowed in a constant expression, but extensions affecting
|
||||
that status may be considered. A similar situation arises for function
|
||||
literals.*
|
||||
|
||||
The transformation consists of two steps. In the first step, every literal
|
||||
list and literal map _e_ which occurs in a constant context and does not
|
||||
have the modifier `const` is replaced by `const` _e_.
|
||||
|
||||
We define *new/const insertion* as the following transformation, which will
|
||||
be applied to specific parts of the program as specified below:
|
||||
|
||||
- if the expression _e_ occurs in a constant context, replace _e_ by
|
||||
`const` _e_,
|
||||
- if the expression _e_ does not occur in a constant context, but `const`
|
||||
_e_ is a correct constant expression, replace _e_ by `const` _e_,
|
||||
- otherwise replace _e_ by `new` _e_.
|
||||
|
||||
*Note that this transformation is applied in a bottom-up order which implies
|
||||
that all relevant transformations have already been applied on subexpressions
|
||||
of _e_. Also note that this transformation is only applied to syntactic
|
||||
constructs where the outcome is a syntactically correct instance creation
|
||||
expression. On the other hand, the outcome may have static semantic errors,
|
||||
e.g., actual arguments to a constructor invocation may have wrong types
|
||||
because that's how the program was written.*
|
||||
|
||||
We define *new insertion* as the following transformation, which will be
|
||||
applied as specified below:
|
||||
|
||||
- replace _e_ by `new` _e_.
|
||||
|
||||
*We specify the second step of the transformation as based on a depth-first
|
||||
traversal of an abstract syntax tree (AST). This means that the program is
|
||||
assumed to be free of syntax errors, and when the current AST is, e.g., a
|
||||
`postfixExpression`, the program as a whole has such a structure that the
|
||||
current location was parsed as a `postfixExpression`. This is different
|
||||
from the situation where we just require that a given subsequence of the
|
||||
tokens of the program allows for such a parsing in isolation. For instance,
|
||||
an identifier like `x` parses as an `assignableExpression` in isolation,
|
||||
but if it occurs in the context `var x = 42;` or `var y = x;` then it will
|
||||
not be parsed as an `assignableExpression`, it will be parsed as a plain
|
||||
`identifier` which is part of a `declaredIdentifier` in the first case, and
|
||||
as a `primary` which is a `postfixExpression`, which is a
|
||||
`unaryExpression`, etc., in the second case. In short, we are transforming
|
||||
the AST of the program as a whole, not isolated snippets of code.*
|
||||
|
||||
*In scientific literature, this kind of transformation is commonly
|
||||
specified as an inductive transformation where `[[e1 e2]] = [[e1]] [[e2]]`
|
||||
when the language supports a construct of the form `e1 e2`, etc. The reader
|
||||
may prefer to view the transformation in that light, and we would then say
|
||||
that we have omitted all the congruence rules.*
|
||||
|
||||
An expression of one of the following forms must be modified in bottom-up
|
||||
order to be or contain a `constantObjectExpression` or `newExpression`
|
||||
as described:
|
||||
|
||||
With a `postfixExpression` _e_,
|
||||
|
||||
- if _e_ is of the form `constructorInvocation selector*`, i.e.,
|
||||
`typeName typeArguments '.' identifier arguments selector*` then perform
|
||||
new/const insertion on the initial `constructorInvocation`.
|
||||
- if _e_ is of the form
|
||||
`typeIdentifier arguments` where `typeIdentifier` denotes a class then
|
||||
perform new/const insertion on _e_.
|
||||
- if _e_ is of the form
|
||||
`identifier1 '.' identifier2 arguments` where `identifier1` denotes
|
||||
a class and `identifier2` is the name of a named constructor in that class,
|
||||
or `identifier1` denotes a prefix for a library _L_ and `identifier2` denotes
|
||||
a class exported by _L_, perform new/const insertion on _e_.
|
||||
- if _e_ is of the form
|
||||
`identifier1 '.' typeIdentifier '.' identifier2 arguments` where
|
||||
`identifier1` denotes a library prefix for a library _L_, `typeIdentifier`
|
||||
denotes a class _C_ exported by _L_, and `identifier2` is the name of a named
|
||||
constructor in _C_, perform new/const insertion on _e_.
|
||||
|
||||
For the purposes of describing the transformation on assignable expressions
|
||||
we need the following syntactic entity:
|
||||
|
||||
```
|
||||
assignableExpressionTail ::=
|
||||
arguments assignableSelector assignableSelectorPart*
|
||||
```
|
||||
|
||||
With an `assignableExpression` _e_,
|
||||
|
||||
- if _e_ is of the form
|
||||
`constructorInvocation assignableSelectorPart+`
|
||||
then perform new/const insertion on the initial
|
||||
`constructorInvocation`.
|
||||
- if _e_ is of the form
|
||||
`typeIdentifier assignableExpressionTail`
|
||||
where `typeIdentifier` denotes a class then perform new/const insertion on
|
||||
the initial `typeIdentifier arguments`.
|
||||
- if _e_ is of the form
|
||||
`typeIdentifier '.' identifier assignableExpressionTail`
|
||||
where `typeIdentifier` denotes a class and `identifier` is the name of
|
||||
a named constructor in that class, or `typeIdentifier` denotes a prefix
|
||||
for a library _L_ and `identifier` denotes a class exported by _L_
|
||||
then perform new/const insertion on the initial
|
||||
`typeIdentifier '.' identifier arguments`.
|
||||
- if _e_ is of the form
|
||||
`typeIdentifier1 '.' typeIdentifier2 '.' identifier assignableExpressionTail`
|
||||
Where `typeIdentifier1` denotes a library prefix for a library _L_,
|
||||
`typeIdentifier2` denotes a class _C_ exported by _L_, and `identifier`
|
||||
is the name of a named constructor in _C_ then perform new/const insertion
|
||||
on the initial
|
||||
`typeIdentifier1 '.' typeIdentifier2 '.' identifier arguments`.
|
||||
|
||||
*In short, add `const` wherever possible on terms that invoke a
|
||||
constructor, and otherwise add `new`. It is easy to verify that each of the
|
||||
replacements can be derived from `postfixExpression` via `primary
|
||||
selector*` and similarly for `assignableExpression`. Hence, the
|
||||
transformation preserves syntactic correctness.*
|
||||
|
||||
|
||||
## Dynamic Semantics
|
||||
|
||||
There is no dynamic semantics to specify for this feature because it is
|
||||
eliminated by code transformation.
|
||||
|
||||
|
||||
## Revisions
|
||||
|
||||
- 0.5 (2018-01-04) Rewritten to use `const` whenever possible (aka "magic
|
||||
const") and adjusted to specify optional const as well as optional new
|
||||
together, because they are now very closely connected. This document was
|
||||
renamed to 'implicit-creation.md', and the document 'optional-const.md'
|
||||
was deleted.
|
||||
|
||||
- 0.4 (2017-10-17) Reverted to use 'immediate subexpression' again, for
|
||||
correctness. Adjusted terminology for consistency. Clarified the semantics
|
||||
of the transformation.
|
||||
|
||||
- 0.3 (2017-09-08) Included missing rule for transformation of composite
|
||||
literals (lists and maps). Eliminated the notion of an immediate
|
||||
subexpression, for improved precision.
|
||||
|
||||
- 0.2 (2017-07-30) Updated the document to specify the previously missing
|
||||
transformations for `assignableExpression`, and to specify a no-magic
|
||||
approach (where no `const` is introduced except when forced by the
|
||||
syntactic context).
|
||||
|
||||
- 0.1 (2017-08-15) Stand-alone informal specification for optional new created,
|
||||
using version 0.8 of the combined proposal
|
||||
[optional-new-const.md](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-new-const.md)
|
||||
as the starting point.
|
|
@ -1,191 +0,0 @@
|
|||
# Optional const
|
||||
|
||||
Author: eernst@.
|
||||
|
||||
Version: 0.3 (2017-09-08)
|
||||
|
||||
Status: Under implementation.
|
||||
|
||||
**This document** is an informal specification of the *optional const* feature.
|
||||
**The feature** adds support for omitting the reserved word `const` in list and
|
||||
map literals and constant object expressions, in locations where `const` is
|
||||
currently required.
|
||||
|
||||
This informal specification is built on a
|
||||
[combined proposal](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-new-const.md)
|
||||
which presents optional const and several other features.
|
||||
|
||||
## Motivation
|
||||
|
||||
In Dart without optional const, complex constant expressions often contain many
|
||||
occurrences of `const` on list and map literals, and on constant object
|
||||
expressions. Subexpressions of constant expressions are themselves required to
|
||||
be constant expressions, and this means that `const` on a nested list or map
|
||||
literal provides no extra information: It is a compile-time error if that
|
||||
`const` is omitted. Similarly, it is a compile-time error if a nested constant
|
||||
object expression is modified to use `new` rather than `const`. In that
|
||||
situation it carries no extra information whether `new` or `const` is used, and
|
||||
it is even possible to omit the reserved word entirely. It is also required for
|
||||
certain other expressions to be constant, e.g., initializing expressions for
|
||||
constant variables.
|
||||
|
||||
In all these cases the presence of `const` is required, and hence such a
|
||||
`const` may be inferred by compilers and similar tools if it is omitted.
|
||||
|
||||
Developers reading the source code are likely to find it easy to understand
|
||||
that a required `const` was omitted and is implied, because the reason for
|
||||
the requirement is visible in the enclosing syntax: The expression where
|
||||
`const` is inferred is a subexpression of an expression with `const` or it
|
||||
is used in another situation where a constant value is required, e.g., to
|
||||
initialize a constant variable.
|
||||
|
||||
In summary, tools do not need the required occurrences of `const`, and they
|
||||
are also unimportant for developers. Conversely, omitting required occurrences
|
||||
of `const` will sometimes make large expressions substantially more concise
|
||||
and readable, and also more convenient to write. Here is an example:
|
||||
|
||||
```dart
|
||||
const myMap = const {
|
||||
"a": const [const C("able"), const C("apple"), const C("axis")],
|
||||
"b": const [const C("banana"), const C("bold"), const C("burglary")],
|
||||
};
|
||||
```
|
||||
|
||||
Removing the required occurrences of `const` yields the following:
|
||||
|
||||
```dart
|
||||
const myMap = {
|
||||
"a": [C("able"), C("apple"), C("axis")],
|
||||
"b": [C("banana"), C("bold"), C("burglary")],
|
||||
};
|
||||
```
|
||||
|
||||
This proposal specifies that these previously required occurrences of `const`
|
||||
can be omitted, and will then be inferred.
|
||||
|
||||
For a more detailed discussion and motivation, please consult the
|
||||
[combined proposal](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-new-const.md)
|
||||
which covers optional const as well as several other proposals. That document
|
||||
was the starting point for this informal specification.
|
||||
|
||||
## Syntax
|
||||
|
||||
In order to support the optional const feature, the Dart grammar is modified as
|
||||
follows.
|
||||
|
||||
```
|
||||
postfixExpression ::=
|
||||
assignableExpression postfixOperator |
|
||||
constructorInvocation | // NEW
|
||||
primary selector*
|
||||
constructorInvocation ::= // NEW
|
||||
typeName typeArguments '.' identifier arguments
|
||||
```
|
||||
|
||||
*The grammar only needs to be adjusted for one case, namely invocations of named
|
||||
constructors for generic classes. In this case we can derive expressions like
|
||||
`const Foo<int>.bar()`, and the corresponding `Foo<int>.bar()` is not derivable
|
||||
in the same situations where the variant with `const` can be derived. In other
|
||||
words, we must add support for constructs like `Foo<int>.bar()` as part of a
|
||||
`postfixExpression`. For all other situations, the variant with `const` becomes
|
||||
a construct which is already syntactically correct Dart when the `const` is
|
||||
removed. For instance `const C(42)` becomes `C(42)` which is already allowed
|
||||
syntactically (it could be a function invocation).*
|
||||
|
||||
## Static analysis
|
||||
|
||||
We specify a type directed source code transformation which eliminates the
|
||||
feature. The static analysis proceeds to work on the transformed program.
|
||||
|
||||
*This means that the feature is "sugar", but because of the need to refer
|
||||
to types it could be described as static semantic sugar rather than
|
||||
syntactic sugar. We do not specify the dynamic semantics for this feature,
|
||||
because the feature is eliminated in this transformation step.*
|
||||
|
||||
We need to treat expressions differently in different locations, hence the
|
||||
following definition: An expression _e_ is said to *occur in a constant
|
||||
context*,
|
||||
|
||||
- if _e_ is an element of a constant list literal, or a key or value of
|
||||
an entry of a constant map literal.
|
||||
- if _e_ is an actual argument of a constant object expression or of a
|
||||
metadata annotation.
|
||||
- if _e_ is the initializing expression of a constant variable declaration.
|
||||
- if _e_ is a switch case expression.
|
||||
- if _e_ is an immediate subexpression of an expression _e1_ which occurs in
|
||||
a constant context, unless _e1_ is a `throw` expression or a function
|
||||
literal.
|
||||
|
||||
*This roughly means that everything which is inside a syntactically
|
||||
constant expression is in a constant context. A `throw` expression is
|
||||
currently not allowed in a constant expression, but extensions affecting
|
||||
that status may be considered. A similar situation arises for function
|
||||
literals.*
|
||||
|
||||
*Note that the default value of an optional formal parameter is not a
|
||||
constant context. This choice reserves some freedom to modify the
|
||||
semantics of default values.*
|
||||
|
||||
An expression on one of the following forms must be modified in top-down order
|
||||
to be or contain a `constantObjectExpression` as described:
|
||||
|
||||
With a `postfixExpression` _e_ occurring in a constant context,
|
||||
|
||||
- if _e_ is on the form `constructorInvocation` then replace _e_ by
|
||||
`const` _e_.
|
||||
- if _e_ is on the form
|
||||
`typeIdentifier arguments` where `typeIdentifier` denotes a class then
|
||||
replace _e_ by `const` _e_.
|
||||
- if _e_ is on the form
|
||||
`identifier1 '.' identifier2 arguments` where `identifier1` denotes
|
||||
a class and `identifier2` is the name of a named constructor in that
|
||||
class, or `identifier1` denotes a prefix for a library _L_ and
|
||||
`identifier2` denotes a class exported by _L_, replace _e_ by
|
||||
`const` _e_.
|
||||
- if _e_ is on the form
|
||||
`identifier1 '.' typeIdentifier '.' identifier2 arguments` where
|
||||
`identifier1` denotes a library prefix for a library _L_,
|
||||
`typeIdentifier` denotes a class _C_ exported by _L_, and `identifier2`
|
||||
is the name of a named constructor in _C_, replace _e_ by
|
||||
`const` _e_.
|
||||
|
||||
For a list literal _e_ occurring in a constant context, replace _e_ by
|
||||
`const` _e_. For a map literal _e_ occurring in a constant context,
|
||||
replace _e_ by `const` _e_.
|
||||
|
||||
*In short, in these specific situations: "just add `const`". It is easy to
|
||||
verify that each of the replacements can be derived from
|
||||
`constObjectExpression`, which can be derived from `postfixExpression` via
|
||||
`primary selector*`. Hence, the transformation preserves syntactic
|
||||
correctness.*
|
||||
|
||||
The remaining static analysis proceeds to work on the transformed program.
|
||||
|
||||
*It is possible that this transformation will create
|
||||
`constObjectExpressions` which violate the constraints on constant object
|
||||
expressions, e.g., when `const [[new A()]]` is transformed to
|
||||
`const [const [new A()]]` where the inner list is an error that was created
|
||||
by the transformation (so the error was moved from the outer to the inner
|
||||
list). It is recommended that the error messages emitted by tools in response
|
||||
to such violations include information about the transformation.*
|
||||
|
||||
## Dynamic Semantics
|
||||
|
||||
There is no dynamic semantics to specify for this feature because it is
|
||||
eliminated by code transformation.
|
||||
|
||||
|
||||
## Revisions
|
||||
|
||||
- 0.3 (2017-09-08) Eliminated the notion of an immediate subexpression,
|
||||
for improved precision.
|
||||
|
||||
- 0.2 (2017-08-30) Updated the document to specify the previously missing
|
||||
transformations for composite literals (lists and maps), and to specify a
|
||||
no-magic approach (where no `const` is introduced except when forced by
|
||||
the syntactic context).
|
||||
|
||||
- 0.1 (2017-08-10) Stand-alone informal specification for optional const
|
||||
created, using version 0.8 of the combined proposal
|
||||
[optional-new-const.md](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-new-const.md)
|
||||
as the starting point.
|
|
@ -1,244 +0,0 @@
|
|||
# Optional new
|
||||
|
||||
Author: eernst@.
|
||||
|
||||
Version: 0.4 (2017-10-17)
|
||||
|
||||
Status: Under implementation.
|
||||
|
||||
**This document** is an informal specification of the *optional new* feature.
|
||||
**The feature** adds support for omitting the reserved word `new` in instance
|
||||
creation expressions.
|
||||
|
||||
This feature extends and includes the
|
||||
[optional const feature](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-const.md),
|
||||
and it is assumed that the reader knows about optional const. Beyond
|
||||
that, this informal specification is derived from a
|
||||
[combined proposal](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-new-const.md)
|
||||
which presents optional new together with several other features.
|
||||
|
||||
|
||||
## Motivation
|
||||
|
||||
In Dart without optional new, the reserved word `new` is present in every
|
||||
expression whose evaluation invokes a constructor (except constant
|
||||
expressions). These expressions are known as *instance creation expressions*. If
|
||||
`new` is removed from such an instance creation expression, the remaining phrase
|
||||
is still syntactically correct in almost all cases. The required grammar
|
||||
update that makes them all syntactically correct is a superset of the one that
|
||||
is specified for
|
||||
[optional const](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-const.md).
|
||||
|
||||
With that grammar update, all instance creation expressions can technically
|
||||
omit the `new` because tools (compilers, analyzers) are able to parse these
|
||||
expressions, and they are able to recognize that they denote instance creations
|
||||
(rather than, say, static function invocations), because the part before the
|
||||
left parenthesis is statically known to denote a constructor.
|
||||
|
||||
For instance, `p.C.foo` may resolve statically to a constructor named `foo` in
|
||||
a class `C` imported with prefix `p`. Similarly, `D` may resolve to a class, in
|
||||
which case `D(42)` is statically known to be a constructor invocation because
|
||||
the other interpretation is statically known to be incorrect (that is, cf.
|
||||
section '16.14.3 Unqualified Invocation' in the language specification,
|
||||
evaluating `(D)(42)`: `(D)` is an instance of `Type` which is not a function
|
||||
type and does not have a method named `call`).
|
||||
|
||||
For human readers, it may be helpful to document that a particular expression
|
||||
is guaranteed to yield a fresh instance, and this is the most common argument
|
||||
why `new` should *not* be omitted. However, Dart already allows instance
|
||||
creation expressions to invoke a factory constructor, so Dart developers never
|
||||
had any firm local guarantees that any particular expression would yield a
|
||||
fresh object.
|
||||
|
||||
Developers may thus prefer to omit `new` in order to obtain more concise code,
|
||||
and possibly also in order to achieve greater uniformity among invocations of
|
||||
constructors and other invocations, e.g., of static or global functions.
|
||||
|
||||
With that in mind, this proposal allows instance creation expressions to omit
|
||||
the `new` in all cases, but also preserves the permission to include `new` in
|
||||
all cases. It is a matter of style to use `new` in a manner that developers
|
||||
find helpful.
|
||||
|
||||
|
||||
## Syntax
|
||||
|
||||
The syntax changes associated with this feature are the following:
|
||||
|
||||
```
|
||||
postfixExpression ::=
|
||||
assignableExpression postfixOperator |
|
||||
constructorInvocation | // NEW
|
||||
primary selector*
|
||||
constructorInvocation ::= // NEW
|
||||
typeName typeArguments '.' identifier arguments
|
||||
assignableExpression ::=
|
||||
SUPER unconditionalAssignableSelector |
|
||||
constructorInvocation (arguments* assignableSelector)+ | // NEW
|
||||
identifier |
|
||||
primary (arguments* assignableSelector)+
|
||||
```
|
||||
|
||||
*As mentioned, this grammar update is a superset of the grammar update for
|
||||
[optional const](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-const.md).*
|
||||
|
||||
|
||||
## Static analysis
|
||||
|
||||
We specify a type directed source code transformation which eliminates the
|
||||
feature by expressing the same semantics with different syntax. The static
|
||||
analysis proceeds to work on the transformed program.
|
||||
|
||||
*Similarly to optional const, this means that the feature is "static semantic
|
||||
sugar". We do not specify the dynamic semantics for this feature, because the
|
||||
feature is eliminated in this transformation step.*
|
||||
|
||||
We need to treat expressions differently in different locations, hence the
|
||||
following definition, which is identical to the one in
|
||||
[optional const](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-const.md):
|
||||
|
||||
An expression _e_ is said to *occur in a constant context*,
|
||||
|
||||
- if _e_ is an element of a constant list literal, or a key or value of
|
||||
an entry of a constant map literal.
|
||||
- if _e_ is an actual argument of a constant object expression or of a
|
||||
metadata annotation.
|
||||
- if _e_ is the initializing expression of a constant variable declaration.
|
||||
- if _e_ is a switch case expression.
|
||||
- if _e_ is an immediate subexpression of an expression _e1_ which occurs in
|
||||
a constant context, unless _e1_ is a `throw` expression or a function
|
||||
literal.
|
||||
|
||||
*This roughly means that everything which is inside a syntactically
|
||||
constant expression is in a constant context. A `throw` expression is
|
||||
currently not allowed in a constant expression, but extensions affecting
|
||||
that status may be considered. A similar situation arises for function
|
||||
literals.*
|
||||
|
||||
We define *new/const insertion* as the following transformation:
|
||||
|
||||
- if the expression _e_ occurs in a constant context, replace _e_ by
|
||||
`const` _e_,
|
||||
- otherwise replace _e_ by `new` _e_.
|
||||
|
||||
We define *new insertion* as the following transformation:
|
||||
|
||||
- replace _e_ by `new` _e_.
|
||||
|
||||
For the purposes of describing the main transformation we need the following
|
||||
syntactic entity:
|
||||
|
||||
```
|
||||
assignableExpressionTail ::=
|
||||
arguments assignableSelector (arguments* assignableSelector)*
|
||||
```
|
||||
|
||||
*We specify the transformation as based on a top-down traversal of an
|
||||
abstract syntax tree (AST). This means that the program is assumed to be
|
||||
free of syntax errors, and when the current AST is, e.g., a
|
||||
`postfixExpression`, the program as a whole has such a structure that
|
||||
the current location was parsed as a `postfixExpression`. This is
|
||||
different from the situation where we just require a given subsequence of the
|
||||
tokens of the program allows for such a parsing in isolation. For instance,
|
||||
an identifier like `x` parses as an `assignableExpression` in isolation,
|
||||
but if it occurs in the context `var x = 42;` or `var y = x;` then it
|
||||
will not be parsed as an `assignableExpression`, it will be parsed as a
|
||||
plain `identifier` which is part of a `declaredIdentifier` in the first
|
||||
case, and as a `primary` which is a `postfixExpression`, which is a
|
||||
`unaryExpression`, etc., in the second case. In short, we are
|
||||
transforming the AST of the program as a whole, not isolated snippets of
|
||||
code.*
|
||||
|
||||
An expression of one of the following forms must be modified in top-down
|
||||
order to be or contain a `constantObjectExpression` or `newExpression`
|
||||
as described:
|
||||
|
||||
With a `postfixExpression` _e_,
|
||||
|
||||
- if _e_ is of the form `constructorInvocation`, i.e.,
|
||||
`typeName typeArguments '.' identifier arguments` then perform
|
||||
new/const insertion on _e_.
|
||||
- if _e_ is of the form
|
||||
`typeIdentifier arguments` where `typeIdentifier` denotes a class then
|
||||
perform new/const insertion on _e_.
|
||||
- if _e_ is of the form
|
||||
`identifier1 '.' identifier2 arguments` where `identifier1` denotes
|
||||
a class and `identifier2` is the name of a named constructor in that class,
|
||||
or `identifier1` denotes a prefix for a library _L_ and `identifier2` denotes
|
||||
a class exported by _L_, perform new/const insertion on _e_.
|
||||
- if _e_ is of the form
|
||||
`identifier1 '.' typeIdentifier '.' identifier2 arguments` where
|
||||
`identifier1` denotes a library prefix for a library _L_, `typeIdentifier`
|
||||
denotes a class _C_ exported by _L_, and `identifier2` is the name of a named
|
||||
constructor in _C_, perform new/const insertion on _e_.
|
||||
|
||||
With an `assignableExpression` _e_,
|
||||
|
||||
- if _e_ is of the form
|
||||
`constructorInvocation assignableExpressionTail`
|
||||
then perform new insertion on the initial
|
||||
`constructorInvocation arguments`.
|
||||
- if _e_ is of the form
|
||||
`typeIdentifier assignableExpressionTail`
|
||||
where `typeIdentifier` denotes a class then perform new insertion on the
|
||||
initial `typeIdentifier arguments`.
|
||||
- if _e_ is of the form
|
||||
`identifier1 '.' identifier2 assignableExpressionTail`
|
||||
where `identifier1` denotes a class and `identifier2` is the name of
|
||||
a named constructor in that class, or `identifier1` denotes a prefix
|
||||
for a library _L_ and `identifier2` denotes a class exported by _L_
|
||||
then perform new insertion on the initial
|
||||
`identifier1 '.' identifier2 arguments`.
|
||||
- if _e_ is of the form
|
||||
`identifier1 '.' typeIdentifier '.' identifier2 assignableExpressionTail`
|
||||
where `identifier1` denotes a library prefix for a library _L_,
|
||||
`typeIdentifier` denotes a class _C_ exported by _L_, and `identifier2`
|
||||
is the name of a named constructor in _C_ then perform new insertion
|
||||
on the initial
|
||||
`identifier1 '.' typeIdentifier '.' identifier2 arguments`.
|
||||
|
||||
For a list literal _e_ occurring in a constant context, replace _e_ by
|
||||
`const` _e_. For a map literal _e_ occurring in a constant context,
|
||||
replace _e_ by `const` _e_.
|
||||
|
||||
*In short, add `const` in const contexts and otherwise add `new`. With
|
||||
`assignableExpression` we always add `new`, because such an expression
|
||||
can never be a subexpression of a correct constant expression. It is easy
|
||||
to verify that each of the replacements can be derived from
|
||||
`postfixExpression` via `primary selector*` and similarly for
|
||||
`assignableExpression`. Hence, the transformation preserves syntactic
|
||||
correctness.*
|
||||
|
||||
|
||||
## Dynamic Semantics
|
||||
|
||||
There is no dynamic semantics to specify for this feature because it is
|
||||
eliminated by code transformation.
|
||||
|
||||
|
||||
## Interplay with optional const
|
||||
|
||||
This informal specification includes optional const as well as optional new,
|
||||
that is, if this specification is implemented then
|
||||
[optional const](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-const.md)
|
||||
may be considered as background material.
|
||||
|
||||
|
||||
## Revisions
|
||||
|
||||
- 0.4 (2017-10-17) Reverted to use 'immediate subexpression' again, for
|
||||
correctness. Adjusted terminology for consistency. Clarified the semantics
|
||||
of the transformation.
|
||||
|
||||
- 0.3 (2017-09-08) Included missing rule for transformation of composite
|
||||
literals (lists and maps). Eliminated the notion of an immediate
|
||||
subexpression, for improved precision.
|
||||
|
||||
- 0.2 (2017-07-30) Updated the document to specify the previously missing
|
||||
transformations for `assignableExpression`, and to specify a no-magic
|
||||
approach (where no `const` is introduced except when forced by the
|
||||
syntactic context).
|
||||
|
||||
- 0.1 (2017-08-15) Stand-alone informal specification for optional new created,
|
||||
using version 0.8 of the combined proposal
|
||||
[optional-new-const.md](https://github.com/dart-lang/sdk/blob/master/docs/language/informal/optional-new-const.md)
|
||||
as the starting point.
|
Loading…
Reference in a new issue