Add specification of set literals.

Bug: 63d164fa7a/accepted/future-releases/set-literals/implementation-plan.md (Task 10)
Change-Id: I9728706e235162318aec824484274aea58210e8a
Reviewed-on: https://dart-review.googlesource.com/c/84600
Commit-Queue: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Leaf Petersen <leafp@google.com>
Reviewed-by: Erik Ernst <eernst@google.com>
This commit is contained in:
Lasse Reichstein Holst Nielsen 2018-12-14 13:15:38 +00:00 committed by commit-bot@chromium.org
parent e98701ea33
commit 8dd03bee13
3 changed files with 198 additions and 19 deletions

View file

@ -27,6 +27,7 @@
% 2.2
% - Specify whether the values of literal expressions override Object.==.
% - Allow Type objects as case expressions and const map keys.
% - Introduce set literals.
%
% 2.1
% - Remove 64-bit constraint on integer literals compiled to JavaScript numbers.
@ -5628,6 +5629,12 @@ not a constant expression (\ref{const}).
that occurs in a constant context, is a potentially constant expression if $T$ is a constant type expression, and $e_1$, \ldots{} , $e_n$ are constant expressions.
It is further a constant expression if the list literal evaluates to a value.
\item A constant set literal (\ref{set}),
\code{\CONST{} <$T$>\{$e_1$, \ldots{}, $e_n$\}}, or
\code{<$T$>\{$e_1$, \ldots{}, $e_n$\}}
that occurs in a constant context, is a potentially constant expression if $T$ is a constant type expression, and $e_1$, \ldots{} , $e_n$ are constant expressions.
It is further a constant expression if the list literal evaluates to a value.
\item A constant map literal (\ref{maps}),
\code{\CONST{} <$K$, $V$>\{$k_1$: $v_1$, \ldots{}, $k_n$: $v_n$\}}, or
\code{<$K$, $V$>\{$k_1$: $v_1$, \ldots{}, $k_n$: $v_n$\}} that occurs in a constant context,
@ -5820,6 +5827,8 @@ As an example, consider:
\alt <stringLiteral>
\alt <symbolLiteral>
\alt <mapLiteral>
\alt <setLiteral>
\alt <setOrMapLiteral>
\alt <listLiteral>
\end{grammar}
@ -6412,15 +6421,36 @@ A \IndexCustom{map literal}{literal!map} denotes a map object.
\begin{grammar}
<mapLiteral> ::= \CONST{}? <typeArguments>?
\gnewline{} `{' (<mapLiteralEntry> (`,' <mapLiteralEntry>)* `,'?)? `\}'
\gnewline{} `{' <mapLiteralEntry> (`,' <mapLiteralEntry>)* `,'? `}'
<mapLiteralEntry> ::= <expression> `:' <expression>
<setOrMapLiteral> ::= \CONST{}? <typeArguments>? `{' `}'
\end{grammar}
\LMHash{}%
A \synt{setOrMapLiteral} is either set literal (\ref {sets}) or a map literal,
determined by the type parameters or static context type.
If the literal expression has exactly one type argument,
then it is a set literal.
If it has two type arguments, then it is a map literal.
If it has three or more type arguments, it is a compile-time error.
If it has \emph{no} type arguments,
then if \code{LinkedHashSet<Null>} is assignable to the
static context type of the literal,
and \code{LinkedHashMap<Null, Null>} is not,
it is set literal,
and otherwise it is a map literal.
A map literal derived from \synt{setOrMapLiteral}
is treated the same way as one derived from \synt{mapLiteral},
as described below.
\LMHash{}%
A map literal consists of zero or more entries.
Each entry has a \Index{key} and a \Index{value}.
Each key and each value is denoted by an expression.
It is a compile-time error if a map literal has one type argument,
or more than two type arguments.
\LMHash{}%
If a map literal begins with the reserved word \CONST{}, it is a
@ -6473,6 +6503,10 @@ If{}f \code{identical($o_{1i}$, $o_{2i}$)} and \code{identical($s_{1i}$, $s_{2i}
In other words, constant map literals are canonicalized.
}
\LMHash{}%
It is a compile-time error if two keys of a constant map literal are equal
according to their \code{==} operator (\ref{equality}).
\LMHash{}%
A run-time map literal
\code{<$K, V$>\{$k_1:e_1, \ldots, k_n:e_n$\}}
@ -6483,8 +6517,8 @@ For each $i \in 1 .. n$ in numeric order,
first the expression $k_i$ is evaluated producing object $u_i$,
and then $e_i$ is evaluated producing object $o_i$.
This produces all the objects $u_1, o_1, \ldots, u_n, o_n$.
\item A fresh instance (\ref{generativeConstructors}) $m$ whose class implements the built-in class
\code{Map<$K, V$>} is allocated.
\item A fresh instance (\ref{generativeConstructors}) $m$
whose class implements the built-in class \code{Map<$K, V$>}, is allocated.
\item
The operator \syntax{`[]='} is invoked on $m$ with first argument $u_i$ and second argument $o_i$ for each $i \in 1 .. n$.
\item
@ -6502,9 +6536,6 @@ is evaluated as
\code{<\DYNAMIC{}, \DYNAMIC{}>\{$k_1:e_1, \ldots, k_n:e_n$\}}.
\LMHash{}%
It is a compile-time error if two keys of a constant map literal are equal.
\LMHash{}%
A map literal is ordered: iterating over the keys and/or values of the maps always happens in the
order the keys appeared in the source code.
@ -6527,6 +6558,121 @@ or the form
\code{Map<\DYNAMIC{}, \DYNAMIC{}>}.
\subsection{Sets}
\LMLabel{sets}
\LMHash{}%
A \IndexCustom{set literal}{literal!set} denotes a set object.
\begin{grammar}
<setLiteral> ::= \CONST{}? <typeArguments>?
\gnewline{} `{' <expression> (`,' <expression>)* `,'? `\}'
\end{grammar}
\LMHash{}%
A \synt{setOrMapLiteral} is either set literal or a map literal (\ref {maps}).
A set literal derived from \synt{setOrMapLiteral}
is treated the same way as one derived from \synt{setLiteral},
as described below.
\LMHash{}%
A set literal consists of zero or more element expressions.
It is a compile-time error if a set literal has more than one type argument.
\LMHash{}%
\rationale{
A set literal with no type argument is always converted to a literal
with a type argument by type inference (\ref{overview}), so the following
section only address the behavior of literals with type arguments.}
\LMHash{}%
If a set literal begins with the reserved word \CONST{},
or if it occurs in a constant context, then it is a
\IndexCustom{constant set literal}{literal!set!constant}
which is a constant expression (\ref{constants}) and therefore evaluated at compile time.
Otherwise, it is a
\IndexCustom{run-time set literal}{literal!set!run-time}
and it is evaluated at run time.
Only run-time set literals can be mutated after they are created.
Attempting to mutate a constant set literal will result in a dynamic error.
\LMHash{}%
It is a compile-time error if an element expression in a constant set literal is not a constant expression.
It is a compile-time error if the element object in a constant set literal is an instance of
a class that has a concrete operator \syntax{`=='} declaration different from the one in \code{Object},
unless the element is a string or an integer,
the element expression evaluates to an instance of the built-in
class \code{Symbol} which was originally obtained by evaluation of a
literal symbol or
a constant invocation of a constructor of the \code{Symbol} class,
or to an object implementing the built-in class \code{Type}
which was originally obtained by evaluating a constant type literal
(\ref{dynamicTypeSystem}).
It is a compile-time error if the type argument of a constant set literal
is not a constant type expression \ref{constants}.
It is a compile-time error if two elements of a constant set literal are equal
according to their \code{==} operator (\ref{equality}).
\LMHash{}%
The value of a constant set literal with element expressions
$e_1, \dots, e_n$ and type argument $E$
is an object $s$ whose class implements the built-in class
\code{Set<$E$>}.
The elements of $m$ are $v_i, i \in 1 .. n$, where $v_i$ is the value of the constant expression $e_i$.
\LMHash{}%
Let $set_1$ be a constant set literal with type argument $E$
and element expressions, in source order, $e_{11}, \ldots, e_{1n}$ evaluating
to values $v_{11}, \ldots, v_{1n}$.
Let $set_2$ be a constant set literal with type argument $F$
and element expressions, in source order, $e_{21}, \ldots, e_{2n}$ evaluating
to values $v_{21}, \ldots, v_{2n}$.
If{}f \code{identical($v_{1i}$, $v_{2i}$)}
for $i \in 1 .. n$, and $E$ and $F$ is the same type,
then \code{identical($set_1$, $set_2$)}.
\commentary{
In other words, constant set literals are canonicalized if they have
the same type and the same values in the same order.
}
Two constant set literals are never identical if they have different numbers
of elements.
\LMHash{}%
A run-time set literal with element expressions $e_1, \ldots, e_n$
(in source order) and with type argument $E$
is evaluated as follows:
\begin{itemize}
\item
For each $i \in 1 .. n$ in numeric order,
the expression $e_i$ is evaluated producing object $v_i$.
\item A fresh instance (\ref{generativeConstructors}) $s$
of the built-in class \code{LinkedHashSet<$E$>}, is allocated.
\item
The operator \code{add} is invoked on $s$ with argument $v_i$ for each $i \in 1 .. n$ in numerical order.
\item
The result of the evaluation is $s$.
\end{itemize}
\LMHash{}%
The objects created by set literals do not override
the \code{==} operator inherited from the \code{Object} class.
\LMHash{}%
A set literal is ordered: iterating over the elements of the sets
always happens in the order the elements first appeared in the source code.
\commentary{
If a value repeats, the order is defined by first occurrence, but the value is defined by the last.
}
\LMHash{}%
The static type of a set literal of the form
\code{\CONST{} <$E$>\{$e_1, \ldots, e_n$\}}
or the form
\code{<$E$>\{$e_1, \ldots, e_n$\}}
is
\code{Set<$E$>}.
\subsection{Throw}
\LMLabel{throw}

View file

@ -0,0 +1,23 @@
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Canary test to check that set literals are not enabled *without* an
// experimental flag.
// Remove this test when the set literals feature is enabled without a flag.
main() {
var _ = {1}; //# 01: compile-time error
var _ = <int>{}; //# 02: compile-time error
Set _ = {}; //# 03: compile-time error
Set _ = <int>{}; //# 04: compile-time error
var _ = const {1}; //# 05: compile-time error
var _ = const <int>{}; //# 06: compile-time error
Set _ = const {}; //# 07: compile-time error
Set _ = const <int>{}; //# 08: compile-time error
const _ = {1}; //# 09: compile-time error
const _ = <int>{}; //# 10: compile-time error
const Set _ = {}; //# 11: compile-time error
const Set _ = <int>{}; //# 12: compile-time error
}

View file

@ -20,8 +20,8 @@ void main() {
= const <int, int, int>{1} //# 06: compile-time error
= const <int, int>{1} //# 07: compile-time error
= const {Duration(seconds: 0)} // Overrides ==. //# 08: compile-time error
= {4.2} // Overrides ==. //# 09: compile-time error
= {d} // Overrides ==. //# 10: compile-time error
= const {4.2} // Overrides ==. //# 09: compile-time error
= const {d} // Overrides ==. //# 10: compile-time error
= {,} //# 11: compile-time error
= {1,,} //# 12: compile-time error
= {1,,1} //# 13: compile-time error
@ -33,7 +33,6 @@ void main() {
= {4.2} //# 15: compile-time error
= {1: 1} //# 16: compile-time error
= {{}} //# 17: compile-time error
= <Object>{} // Exact type. //# 18: compile-time error
;
Expect.isNull(s);
@ -50,16 +49,6 @@ void main() {
;
Expect.isNull(hs);
LinkedHashSet<int> lhs //
= const {} // exact type is Set //# 24: compile-time error
;
Expect.isNull(lhs);
LinkedHashSet<LinkedHashSet<int>> lhs2 //
= {const {}} // exact type LHS<Set>. //# 25: compile-time error
;
Expect.isNull(lhs2);
<T>(x) {
// Type constants are allowed, type variables are not.
var o //
@ -77,4 +66,25 @@ void main() {
= {}; //# 28: compile-time error
;
}();
// Constant sets must not contain equal elements.
const s = {
1,
"s",
#foo,
int,
C(1),
{1},
1, //# 29: compile-time error
"s", //# 30: compile-time error
#foo, //# 31: compile-time error
int, //# 32: compile-time error
C(1), //# 33: compile-time error
{1}, //# 34: compile-time error
};
}
class C {
final Object id;
const C(this.id);
}