diff --git a/docs/language/dartLangSpec.tex b/docs/language/dartLangSpec.tex index b03f2a3f37a..c9e206d8509 100644 --- a/docs/language/dartLangSpec.tex +++ b/docs/language/dartLangSpec.tex @@ -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 \alt \alt + \alt + \alt \alt \end{grammar} @@ -6412,15 +6421,36 @@ A \IndexCustom{map literal}{literal!map} denotes a map object. \begin{grammar} ::= \CONST{}? ? - \gnewline{} `{' ( (`,' )* `,'?)? `\}' + \gnewline{} `{' (`,' )* `,'? `}' ::= `:' + + ::= \CONST{}? ? `{' `}' \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} is assignable to the +static context type of the literal, +and \code{LinkedHashMap} 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} + ::= \CONST{}? ? + \gnewline{} `{' (`,' )* `,'? `\}' +\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} diff --git a/tests/language_2/set_literals/const_set_flag_test.dart b/tests/language_2/set_literals/const_set_flag_test.dart new file mode 100644 index 00000000000..339acafbb63 --- /dev/null +++ b/tests/language_2/set_literals/const_set_flag_test.dart @@ -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 _ = {}; //# 02: compile-time error + Set _ = {}; //# 03: compile-time error + Set _ = {}; //# 04: compile-time error + var _ = const {1}; //# 05: compile-time error + var _ = const {}; //# 06: compile-time error + Set _ = const {}; //# 07: compile-time error + Set _ = const {}; //# 08: compile-time error + const _ = {1}; //# 09: compile-time error + const _ = {}; //# 10: compile-time error + const Set _ = {}; //# 11: compile-time error + const Set _ = {}; //# 12: compile-time error +} diff --git a/tests/language_2/set_literals/invalid_set_literal_test.dart b/tests/language_2/set_literals/invalid_set_literal_test.dart index 0d8cd43c7d0..4d83e7581dd 100644 --- a/tests/language_2/set_literals/invalid_set_literal_test.dart +++ b/tests/language_2/set_literals/invalid_set_literal_test.dart @@ -20,8 +20,8 @@ void main() { = const {1} //# 06: compile-time error = const {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 - = {} // Exact type. //# 18: compile-time error ; Expect.isNull(s); @@ -50,16 +49,6 @@ void main() { ; Expect.isNull(hs); - LinkedHashSet lhs // - = const {} // exact type is Set //# 24: compile-time error - ; - Expect.isNull(lhs); - - LinkedHashSet> lhs2 // - = {const {}} // exact type LHS. //# 25: compile-time error - ; - Expect.isNull(lhs2); - (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); }