[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>
This commit is contained in:
Kallen Tu 2021-04-20 20:19:18 +00:00 committed by commit-bot@chromium.org
parent 97e9186df8
commit 95229aa779
10 changed files with 371 additions and 0 deletions

View file

@ -2218,6 +2218,44 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
}
} 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(

View file

@ -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);
}

View file

@ -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}
}

View file

@ -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}
}

View file

@ -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() {}

View file

@ -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() {}

View file

@ -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}
}

View file

@ -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

View file

@ -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}
}

View file

@ -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);
}