Add specification of set literals.

Bug: https://github.com/dart-lang/language/blob/63d164fa7a507b28aace20fffe31db2c3c9baa2d/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>
diff --git a/docs/language/dartLangSpec.tex b/docs/language/dartLangSpec.tex
index b03f2a3..c9e206d 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 @@
 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 @@
   \alt <stringLiteral>
   \alt <symbolLiteral>
   \alt <mapLiteral>
+  \alt <setLiteral>
+  \alt <setOrMapLiteral>
   \alt <listLiteral>
 \end{grammar}
 
@@ -6412,15 +6421,36 @@
 
 \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
@@ -6474,6 +6504,10 @@
 }
 
 \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$\}}
 is evaluated as follows:
@@ -6483,8 +6517,8 @@
 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
@@ -6503,9 +6537,6 @@
 \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 @@
 \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}
 
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 0000000..339acaf
--- /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 _ = <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
+}
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 0d8cd43..4d83e75 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 @@
       = 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 @@
       = {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 @@
       ;
   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 @@
     = {}; //# 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);
 }