blob: 9b796492196fee0c8e19b7bc42277b1f60f81375 [file] [log] [blame] [view]
# 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.