Eliminated the imprecise notion of an immediate subexpression. Included missing rule for transformation of composite literals (lists and maps). Some smaller fixes according to feedback by mail. Change-Id: I03e58dd24b370b797cda084bd064c6f0db22f8fb Reviewed-on: https://dart-review.googlesource.com/4383 Reviewed-by: Lasse R.H. Nielsen <lrn@google.com> Reviewed-by: Bob Nystrom <rnystrom@google.com>
8.2 KiB
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 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:
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:
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 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
throwexpression 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
constructorInvocationthen replace e byconste. - if e is on the form
typeIdentifier argumentswheretypeIdentifierdenotes a class then replace e byconste. - if e is on the form
identifier1 '.' identifier2 argumentswhereidentifier1denotes a class andidentifier2is the name of a named constructor in that class, oridentifier1denotes a prefix for a library L andidentifier2denotes a class exported by L, replace e byconste. - if e is on the form
identifier1 '.' typeIdentifier '.' identifier2 argumentswhereidentifier1denotes a library prefix for a library L,typeIdentifierdenotes a class C exported by L, andidentifier2is the name of a named constructor in C, replace e byconste.
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
constis 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 as the starting point.