[cfe] Map [] operator enabled for const functions.

Change-Id: I19b0980fc100b3cd19da1875ec9fb08cdd1de70b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/195620
Reviewed-by: Jake Macdonald <jakemac@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 315781f..57ba7b1 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -2218,6 +2218,44 @@
       }
     } else if (receiver is NullConstant) {
       return createErrorConstant(node, messageConstEvalNullValue);
+    } else if (receiver is MapConstant && enableConstFunctions) {
+      if (arguments.length == 1) {
+        final Constant other = arguments[0];
+        switch (op) {
+          case '[]':
+            final ConstantMapEntry mapEntry = receiver.entries
+                .firstWhere((entry) => entry.key == other, orElse: () => null);
+            // Null value if key is not in the map.
+            return mapEntry?.value ?? new NullConstant();
+        }
+      }
+    } else if (receiver is InstanceConstant && enableConstFunctions) {
+      if (arguments.length == 1) {
+        final Constant other = arguments[0];
+        if (receiver.classNode.name == '_ImmutableMap') {
+          switch (op) {
+            case '[]':
+              final ListConstant values = receiver.fieldValues.entries
+                  .firstWhere(
+                      (entry) => entry.key.canonicalName.name == '_kvPairs',
+                      orElse: () => null)
+                  .value;
+              assert(values != null);
+
+              // Each i index element in [values] is a key whose value is the
+              // i+1 index element.
+              int keyIndex = values.entries.indexOf(other);
+              if (keyIndex != -1) {
+                int valueIndex = keyIndex + 1;
+                assert(valueIndex != values.entries.length);
+                return values.entries[valueIndex];
+              } else {
+                // Null value if key is not in the map.
+                return new NullConstant();
+              }
+          }
+        }
+      }
     }
 
     return createErrorConstant(
diff --git a/pkg/front_end/testcases/const_functions/const_functions_map.dart b/pkg/front_end/testcases/const_functions/const_functions_map.dart
new file mode 100644
index 0000000..5fbdd6b
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_map.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2021, 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.
+
+// Tests map usage with const functions.
+
+import "package:expect/expect.dart";
+
+const var1 = fn({'key': 'val'}, 'key');
+
+const var2 = fn({'key': 2}, 'key');
+
+const var3 = fn({'key': 2}, 'invalid');
+
+const map = {'key1': 2, 'key2': 3, 'key3': 4};
+const var4 = fn(map, 'key1');
+const var5 = fn(map, 'key2');
+const var6 = fn(map, 'key3');
+
+Object? fn(Map<Object, Object> map, Object key) {
+  return map[key];
+}
+
+const var7 = fn2();
+int? fn2() {
+  const y = {'key': 2};
+  return y['key'];
+}
+
+void main() {
+  Expect.equals(var1, 'val');
+  Expect.equals(var2, 2);
+  Expect.equals(var3, null);
+  Expect.equals(var4, 2);
+  Expect.equals(var5, 3);
+  Expect.equals(var6, 4);
+  Expect.equals(var7, 2);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_map.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_map.dart.strong.expect
new file mode 100644
index 0000000..1b2b117
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_map.dart.strong.expect
@@ -0,0 +1,46 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::Object? var1 = #C1;
+static const field core::Object? var2 = #C2;
+static const field core::Object? var3 = #C3;
+static const field core::Map<core::String, core::int> map = #C10;
+static const field core::Object? var4 = #C2;
+static const field core::Object? var5 = #C6;
+static const field core::Object? var6 = #C8;
+static const field core::int? var7 = #C2;
+static method fn(core::Map<core::Object, core::Object> map, core::Object key) → core::Object? {
+  return map.{core::Map::[]}(key);
+}
+static method fn2() → core::int? {
+  return (#C13).{core::Map::[]}("key");
+}
+static method main() → void {
+  exp::Expect::equals(#C1, "val");
+  exp::Expect::equals(#C2, 2);
+  exp::Expect::equals(#C3, null);
+  exp::Expect::equals(#C2, 2);
+  exp::Expect::equals(#C6, 3);
+  exp::Expect::equals(#C8, 4);
+  exp::Expect::equals(#C2, 2);
+}
+
+constants  {
+  #C1 = "val"
+  #C2 = 2
+  #C3 = null
+  #C4 = "key1"
+  #C5 = "key2"
+  #C6 = 3
+  #C7 = "key3"
+  #C8 = 4
+  #C9 = <dynamic>[#C4, #C2, #C5, #C6, #C7, #C8]
+  #C10 = core::_ImmutableMap<core::String, core::int> {_kvPairs:#C9}
+  #C11 = "key"
+  #C12 = <dynamic>[#C11, #C2]
+  #C13 = core::_ImmutableMap<core::String, core::int> {_kvPairs:#C12}
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_map.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_map.dart.strong.transformed.expect
new file mode 100644
index 0000000..1b2b117
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_map.dart.strong.transformed.expect
@@ -0,0 +1,46 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::Object? var1 = #C1;
+static const field core::Object? var2 = #C2;
+static const field core::Object? var3 = #C3;
+static const field core::Map<core::String, core::int> map = #C10;
+static const field core::Object? var4 = #C2;
+static const field core::Object? var5 = #C6;
+static const field core::Object? var6 = #C8;
+static const field core::int? var7 = #C2;
+static method fn(core::Map<core::Object, core::Object> map, core::Object key) → core::Object? {
+  return map.{core::Map::[]}(key);
+}
+static method fn2() → core::int? {
+  return (#C13).{core::Map::[]}("key");
+}
+static method main() → void {
+  exp::Expect::equals(#C1, "val");
+  exp::Expect::equals(#C2, 2);
+  exp::Expect::equals(#C3, null);
+  exp::Expect::equals(#C2, 2);
+  exp::Expect::equals(#C6, 3);
+  exp::Expect::equals(#C8, 4);
+  exp::Expect::equals(#C2, 2);
+}
+
+constants  {
+  #C1 = "val"
+  #C2 = 2
+  #C3 = null
+  #C4 = "key1"
+  #C5 = "key2"
+  #C6 = 3
+  #C7 = "key3"
+  #C8 = 4
+  #C9 = <dynamic>[#C4, #C2, #C5, #C6, #C7, #C8]
+  #C10 = core::_ImmutableMap<core::String, core::int> {_kvPairs:#C9}
+  #C11 = "key"
+  #C12 = <dynamic>[#C11, #C2]
+  #C13 = core::_ImmutableMap<core::String, core::int> {_kvPairs:#C12}
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_map.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_map.dart.textual_outline.expect
new file mode 100644
index 0000000..1a8595a
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_map.dart.textual_outline.expect
@@ -0,0 +1,13 @@
+import "package:expect/expect.dart";
+
+const var1 = fn({'key': 'val'}, 'key');
+const var2 = fn({'key': 2}, 'key');
+const var3 = fn({'key': 2}, 'invalid');
+const map = {'key1': 2, 'key2': 3, 'key3': 4};
+const var4 = fn(map, 'key1');
+const var5 = fn(map, 'key2');
+const var6 = fn(map, 'key3');
+Object? fn(Map<Object, Object> map, Object key) {}
+const var7 = fn2();
+int? fn2() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_map.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_map.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..4f5d64b
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_map.dart.textual_outline_modelled.expect
@@ -0,0 +1,13 @@
+import "package:expect/expect.dart";
+
+Object? fn(Map<Object, Object> map, Object key) {}
+const map = {'key1': 2, 'key2': 3, 'key3': 4};
+const var1 = fn({'key': 'val'}, 'key');
+const var2 = fn({'key': 2}, 'key');
+const var3 = fn({'key': 2}, 'invalid');
+const var4 = fn(map, 'key1');
+const var5 = fn(map, 'key2');
+const var6 = fn(map, 'key3');
+const var7 = fn2();
+int? fn2() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_map.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_map.dart.weak.expect
new file mode 100644
index 0000000..836d428
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_map.dart.weak.expect
@@ -0,0 +1,46 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::Object? var1 = #C1;
+static const field core::Object? var2 = #C2;
+static const field core::Object? var3 = #C3;
+static const field core::Map<core::String, core::int> map = #C10;
+static const field core::Object? var4 = #C2;
+static const field core::Object? var5 = #C6;
+static const field core::Object? var6 = #C8;
+static const field core::int? var7 = #C2;
+static method fn(core::Map<core::Object, core::Object> map, core::Object key) → core::Object? {
+  return map.{core::Map::[]}(key);
+}
+static method fn2() → core::int? {
+  return (#C13).{core::Map::[]}("key");
+}
+static method main() → void {
+  exp::Expect::equals(#C1, "val");
+  exp::Expect::equals(#C2, 2);
+  exp::Expect::equals(#C3, null);
+  exp::Expect::equals(#C2, 2);
+  exp::Expect::equals(#C6, 3);
+  exp::Expect::equals(#C8, 4);
+  exp::Expect::equals(#C2, 2);
+}
+
+constants  {
+  #C1 = "val"
+  #C2 = 2
+  #C3 = null
+  #C4 = "key1"
+  #C5 = "key2"
+  #C6 = 3
+  #C7 = "key3"
+  #C8 = 4
+  #C9 = <dynamic>[#C4, #C2, #C5, #C6, #C7, #C8]
+  #C10 = core::_ImmutableMap<core::String*, core::int*> {_kvPairs:#C9}
+  #C11 = "key"
+  #C12 = <dynamic>[#C11, #C2]
+  #C13 = core::_ImmutableMap<core::String*, core::int*> {_kvPairs:#C12}
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_map.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_map.dart.weak.outline.expect
new file mode 100644
index 0000000..87b21de
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_map.dart.weak.outline.expect
@@ -0,0 +1,31 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::Object? var1 = self::fn(const <core::Object, core::Object>{"key": "val"}, "key");
+static const field core::Object? var2 = self::fn(const <core::Object, core::Object>{"key": 2}, "key");
+static const field core::Object? var3 = self::fn(const <core::Object, core::Object>{"key": 2}, "invalid");
+static const field core::Map<core::String, core::int> map = const <core::String, core::int>{"key1": 2, "key2": 3, "key3": 4};
+static const field core::Object? var4 = self::fn(self::map, "key1");
+static const field core::Object? var5 = self::fn(self::map, "key2");
+static const field core::Object? var6 = self::fn(self::map, "key3");
+static const field core::int? var7 = self::fn2();
+static method fn(core::Map<core::Object, core::Object> map, core::Object key) → core::Object?
+  ;
+static method fn2() → core::int?
+  ;
+static method main() → void
+  ;
+
+
+Extra constant evaluation status:
+Evaluated: MapLiteral @ org-dartlang-testcase:///const_functions_map.dart:9:17 -> InstanceConstant(const _ImmutableMap<Object*, Object*>{_ImmutableMap._kvPairs: const <dynamic>["key", "val"]})
+Evaluated: MapLiteral @ org-dartlang-testcase:///const_functions_map.dart:11:17 -> InstanceConstant(const _ImmutableMap<Object*, Object*>{_ImmutableMap._kvPairs: const <dynamic>["key", 2]})
+Evaluated: MapLiteral @ org-dartlang-testcase:///const_functions_map.dart:13:17 -> InstanceConstant(const _ImmutableMap<Object*, Object*>{_ImmutableMap._kvPairs: const <dynamic>["key", 2]})
+Evaluated: MapLiteral @ org-dartlang-testcase:///const_functions_map.dart:15:13 -> InstanceConstant(const _ImmutableMap<String*, int*>{_ImmutableMap._kvPairs: const <dynamic>["key1", 2, "key2", 3, "key3", 4]})
+Evaluated: StaticGet @ org-dartlang-testcase:///const_functions_map.dart:16:17 -> InstanceConstant(const _ImmutableMap<String*, int*>{_ImmutableMap._kvPairs: const <dynamic>["key1", 2, "key2", 3, "key3", 4]})
+Evaluated: StaticGet @ org-dartlang-testcase:///const_functions_map.dart:17:17 -> InstanceConstant(const _ImmutableMap<String*, int*>{_ImmutableMap._kvPairs: const <dynamic>["key1", 2, "key2", 3, "key3", 4]})
+Evaluated: StaticGet @ org-dartlang-testcase:///const_functions_map.dart:18:17 -> InstanceConstant(const _ImmutableMap<String*, int*>{_ImmutableMap._kvPairs: const <dynamic>["key1", 2, "key2", 3, "key3", 4]})
+Extra constant evaluation: evaluated: 14, effectively constant: 7
diff --git a/pkg/front_end/testcases/const_functions/const_functions_map.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_map.dart.weak.transformed.expect
new file mode 100644
index 0000000..836d428
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_map.dart.weak.transformed.expect
@@ -0,0 +1,46 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::Object? var1 = #C1;
+static const field core::Object? var2 = #C2;
+static const field core::Object? var3 = #C3;
+static const field core::Map<core::String, core::int> map = #C10;
+static const field core::Object? var4 = #C2;
+static const field core::Object? var5 = #C6;
+static const field core::Object? var6 = #C8;
+static const field core::int? var7 = #C2;
+static method fn(core::Map<core::Object, core::Object> map, core::Object key) → core::Object? {
+  return map.{core::Map::[]}(key);
+}
+static method fn2() → core::int? {
+  return (#C13).{core::Map::[]}("key");
+}
+static method main() → void {
+  exp::Expect::equals(#C1, "val");
+  exp::Expect::equals(#C2, 2);
+  exp::Expect::equals(#C3, null);
+  exp::Expect::equals(#C2, 2);
+  exp::Expect::equals(#C6, 3);
+  exp::Expect::equals(#C8, 4);
+  exp::Expect::equals(#C2, 2);
+}
+
+constants  {
+  #C1 = "val"
+  #C2 = 2
+  #C3 = null
+  #C4 = "key1"
+  #C5 = "key2"
+  #C6 = 3
+  #C7 = "key3"
+  #C8 = 4
+  #C9 = <dynamic>[#C4, #C2, #C5, #C6, #C7, #C8]
+  #C10 = core::_ImmutableMap<core::String*, core::int*> {_kvPairs:#C9}
+  #C11 = "key"
+  #C12 = <dynamic>[#C11, #C2]
+  #C13 = core::_ImmutableMap<core::String*, core::int*> {_kvPairs:#C12}
+}
diff --git a/tests/language/const_functions/const_functions_map_test.dart b/tests/language/const_functions/const_functions_map_test.dart
new file mode 100644
index 0000000..1dae725
--- /dev/null
+++ b/tests/language/const_functions/const_functions_map_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2021, 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.
+
+// Tests map usage with const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = fn({'key': 'val'}, 'key');
+//           ^^^^^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+
+const var2 = fn({'key': 2}, 'key');
+//           ^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+
+const var3 = fn({'key': 2}, 'invalid');
+//           ^^^^^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+
+const map = {'key1': 2, 'key2': 3, 'key3': 4};
+const var4 = fn(map, 'key1');
+//           ^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var5 = fn(map, 'key2');
+//           ^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var6 = fn(map, 'key3');
+//           ^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+
+Object? fn(Map<Object, Object> map, Object key) {
+  return map[key];
+}
+
+const var7 = fn2();
+//           ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int? fn2() {
+  const y = {'key': 2};
+  return y['key'];
+}
+
+void main() {
+  Expect.equals(var1, 'val');
+  Expect.equals(var2, 2);
+  Expect.equals(var3, null);
+  Expect.equals(var4, 2);
+  Expect.equals(var5, 3);
+  Expect.equals(var6, 4);
+  Expect.equals(var7, 2);
+}