[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);
+}