Skip to content

Commit 2d330c7

Browse files
kallentucommit-bot@chromium.org
authored andcommitted
[cfe] String [] operator for const functions.
Change-Id: Ia1ad96cd77d94c096bf8a10aa0377f37c79ed398 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/195560 Commit-Queue: Kallen Tu <[email protected]> Reviewed-by: Dmitry Stefantsov <[email protected]> Reviewed-by: Jake Macdonald <[email protected]>
1 parent 58eb386 commit 2d330c7

19 files changed

+721
-1
lines changed

pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2089,9 +2089,9 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
20892089
// This is a white-listed set of methods we need to support on constants.
20902090
if (receiver is StringConstant) {
20912091
if (arguments.length == 1) {
2092+
final Constant other = arguments[0];
20922093
switch (op) {
20932094
case '+':
2094-
final Constant other = arguments[0];
20952095
if (other is StringConstant) {
20962096
return canonicalize(
20972097
new StringConstant(receiver.value + other.value));
@@ -2104,6 +2104,32 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
21042104
typeEnvironment.coreTypes.stringLegacyRawType,
21052105
other.getType(_staticTypeContext),
21062106
isNonNullableByDefault));
2107+
case '[]':
2108+
if (enableConstFunctions) {
2109+
if (intFolder.isInt(other)) {
2110+
int index;
2111+
if (intFolder is JsConstantIntFolder) {
2112+
index = (other as DoubleConstant).value.toInt();
2113+
} else if (intFolder is VmConstantIntFolder) {
2114+
index = (other as IntConstant).value;
2115+
}
2116+
assert(index != null);
2117+
2118+
if (index < 0 || index >= receiver.value.length) {
2119+
return new _AbortDueToThrowConstant(
2120+
node, new RangeError.index(index, receiver.value));
2121+
}
2122+
return canonicalize(new StringConstant(receiver.value[index]));
2123+
}
2124+
return createErrorConstant(
2125+
node,
2126+
templateConstEvalInvalidBinaryOperandType.withArguments(
2127+
'[]',
2128+
receiver,
2129+
typeEnvironment.coreTypes.intNonNullableRawType,
2130+
other.getType(_staticTypeContext),
2131+
isNonNullableByDefault));
2132+
}
21072133
}
21082134
}
21092135
} else if (intFolder.isInt(receiver)) {
@@ -3644,6 +3670,12 @@ class StatementConstantEvaluator extends StatementVisitor<ExecutionStatus> {
36443670
.firstWhere((Class klass) => klass.name == 'StateError');
36453671
throwType =
36463672
new InterfaceType(stateErrorClass, Nullability.nonNullable);
3673+
} else if (throwValue is RangeError) {
3674+
final Class rangeErrorClass = exprEvaluator
3675+
.coreTypes.coreLibrary.classes
3676+
.firstWhere((Class klass) => klass.name == 'RangeError');
3677+
throwType =
3678+
new InterfaceType(rangeErrorClass, Nullability.nonNullable);
36473679
}
36483680
assert(throwType != null);
36493681

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Tests string usage with const functions.
6+
7+
import "package:expect/expect.dart";
8+
9+
const String str = "str";
10+
const var1 = str[2];
11+
12+
const var2 = fn();
13+
fn() {
14+
String local = "str";
15+
return local[0];
16+
}
17+
18+
const var3 = "str"[0];
19+
20+
const var4 = fn2();
21+
fn2() {
22+
try {
23+
var x = str[-1];
24+
} on RangeError {
25+
return 2;
26+
}
27+
}
28+
29+
void main() {
30+
Expect.equals(var1, 'r');
31+
Expect.equals(var2, 's');
32+
Expect.equals(var3, 's');
33+
Expect.equals(var4, 2);
34+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "package:expect/expect.dart" as exp;
5+
6+
import "package:expect/expect.dart";
7+
8+
static const field core::String str = #C1;
9+
static const field core::String var1 = #C2;
10+
static const field dynamic var2 = #C3;
11+
static const field core::String var3 = #C3;
12+
static const field dynamic var4 = #C4;
13+
static method fn() → dynamic {
14+
core::String local = "str";
15+
return local.{core::String::[]}(0);
16+
}
17+
static method fn2() → dynamic {
18+
try {
19+
core::String x = (#C1).{core::String::[]}(1.{core::int::unary-}());
20+
}
21+
on core::RangeError catch(no-exception-var) {
22+
return 2;
23+
}
24+
}
25+
static method main() → void {
26+
exp::Expect::equals(#C2, "r");
27+
exp::Expect::equals(#C3, "s");
28+
exp::Expect::equals(#C3, "s");
29+
exp::Expect::equals(#C4, 2);
30+
}
31+
32+
constants {
33+
#C1 = "str"
34+
#C2 = "r"
35+
#C3 = "s"
36+
#C4 = 2
37+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "package:expect/expect.dart" as exp;
5+
6+
import "package:expect/expect.dart";
7+
8+
static const field core::String str = #C1;
9+
static const field core::String var1 = #C2;
10+
static const field dynamic var2 = #C3;
11+
static const field core::String var3 = #C3;
12+
static const field dynamic var4 = #C4;
13+
static method fn() → dynamic {
14+
core::String local = "str";
15+
return local.{core::String::[]}(0);
16+
}
17+
static method fn2() → dynamic {
18+
try {
19+
core::String x = (#C1).{core::String::[]}(1.{core::int::unary-}());
20+
}
21+
on core::RangeError catch(no-exception-var) {
22+
return 2;
23+
}
24+
}
25+
static method main() → void {
26+
exp::Expect::equals(#C2, "r");
27+
exp::Expect::equals(#C3, "s");
28+
exp::Expect::equals(#C3, "s");
29+
exp::Expect::equals(#C4, 2);
30+
}
31+
32+
constants {
33+
#C1 = "str"
34+
#C2 = "r"
35+
#C3 = "s"
36+
#C4 = 2
37+
}
38+
39+
Extra constant evaluation status:
40+
Evaluated: MethodInvocation @ org-dartlang-testcase:///const_functions_string.dart:23:17 -> IntConstant(-1)
41+
Extra constant evaluation: evaluated: 8, effectively constant: 1
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import "package:expect/expect.dart";
2+
3+
const String str = "str";
4+
const var1 = str[2];
5+
const var2 = fn();
6+
fn() {}
7+
const var3 = "str"[0];
8+
const var4 = fn2();
9+
fn2() {}
10+
void main() {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import "package:expect/expect.dart";
2+
3+
const String str = "str";
4+
const var1 = str[2];
5+
const var2 = fn();
6+
const var3 = "str"[0];
7+
const var4 = fn2();
8+
fn() {}
9+
fn2() {}
10+
void main() {}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "package:expect/expect.dart" as exp;
5+
6+
import "package:expect/expect.dart";
7+
8+
static const field core::String str = #C1;
9+
static const field core::String var1 = #C2;
10+
static const field dynamic var2 = #C3;
11+
static const field core::String var3 = #C3;
12+
static const field dynamic var4 = #C4;
13+
static method fn() → dynamic {
14+
core::String local = "str";
15+
return local.{core::String::[]}(0);
16+
}
17+
static method fn2() → dynamic {
18+
try {
19+
core::String x = (#C1).{core::String::[]}(1.{core::int::unary-}());
20+
}
21+
on core::RangeError catch(no-exception-var) {
22+
return 2;
23+
}
24+
}
25+
static method main() → void {
26+
exp::Expect::equals(#C2, "r");
27+
exp::Expect::equals(#C3, "s");
28+
exp::Expect::equals(#C3, "s");
29+
exp::Expect::equals(#C4, 2);
30+
}
31+
32+
constants {
33+
#C1 = "str"
34+
#C2 = "r"
35+
#C3 = "s"
36+
#C4 = 2
37+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
import "package:expect/expect.dart";
6+
7+
static const field core::String str = "str";
8+
static const field core::String var1 = self::str.{core::String::[]}(2);
9+
static const field dynamic var2 = self::fn();
10+
static const field core::String var3 = "str".{core::String::[]}(0);
11+
static const field dynamic var4 = self::fn2();
12+
static method fn() → dynamic
13+
;
14+
static method fn2() → dynamic
15+
;
16+
static method main() → void
17+
;
18+
19+
20+
Extra constant evaluation status:
21+
Evaluated: StaticGet @ org-dartlang-testcase:///const_functions_string.dart:10:14 -> StringConstant("str")
22+
Extra constant evaluation: evaluated: 5, effectively constant: 1
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "package:expect/expect.dart" as exp;
5+
6+
import "package:expect/expect.dart";
7+
8+
static const field core::String str = #C1;
9+
static const field core::String var1 = #C2;
10+
static const field dynamic var2 = #C3;
11+
static const field core::String var3 = #C3;
12+
static const field dynamic var4 = #C4;
13+
static method fn() → dynamic {
14+
core::String local = "str";
15+
return local.{core::String::[]}(0);
16+
}
17+
static method fn2() → dynamic {
18+
try {
19+
core::String x = (#C1).{core::String::[]}(1.{core::int::unary-}());
20+
}
21+
on core::RangeError catch(no-exception-var) {
22+
return 2;
23+
}
24+
}
25+
static method main() → void {
26+
exp::Expect::equals(#C2, "r");
27+
exp::Expect::equals(#C3, "s");
28+
exp::Expect::equals(#C3, "s");
29+
exp::Expect::equals(#C4, 2);
30+
}
31+
32+
constants {
33+
#C1 = "str"
34+
#C2 = "r"
35+
#C3 = "s"
36+
#C4 = 2
37+
}
38+
39+
Extra constant evaluation status:
40+
Evaluated: MethodInvocation @ org-dartlang-testcase:///const_functions_string.dart:23:17 -> IntConstant(-1)
41+
Extra constant evaluation: evaluated: 8, effectively constant: 1
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Tests erroneous string usage with const functions.
6+
7+
import "package:expect/expect.dart";
8+
9+
const String str = "str";
10+
const var1 = str[-1];
11+
const var2 = str[3];
12+
13+
const var3 = fn();
14+
fn() {
15+
String s = "str";
16+
return str[-1];
17+
}
18+
19+
const var4 = fn2();
20+
fn2() {
21+
String s = "str";
22+
return str[3];
23+
}
24+
25+
const var5 = fn3();
26+
fn3() {
27+
String s = "str";
28+
return str[1.1];
29+
}
30+
31+
void main() {}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
library /*isNonNullableByDefault*/;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/const_functions/const_functions_string_error.dart:28:14: Error: A value of type 'double' can't be assigned to a variable of type 'int'.
6+
// return str[1.1];
7+
// ^
8+
//
9+
// pkg/front_end/testcases/const_functions/const_functions_string_error.dart:10:17: Error: Constant evaluation error:
10+
// const var1 = str[-1];
11+
// ^
12+
// pkg/front_end/testcases/const_functions/const_functions_string_error.dart:10:17: Context: Unhandled core exception: RangeError: Index out of range: index must not be negative: -1
13+
// const var1 = str[-1];
14+
// ^
15+
//
16+
// pkg/front_end/testcases/const_functions/const_functions_string_error.dart:11:17: Error: Constant evaluation error:
17+
// const var2 = str[3];
18+
// ^
19+
// pkg/front_end/testcases/const_functions/const_functions_string_error.dart:11:17: Context: Unhandled core exception: RangeError: Index out of range: index should be less than 3: 3
20+
// const var2 = str[3];
21+
// ^
22+
//
23+
// pkg/front_end/testcases/const_functions/const_functions_string_error.dart:13:14: Error: Constant evaluation error:
24+
// const var3 = fn();
25+
// ^
26+
// pkg/front_end/testcases/const_functions/const_functions_string_error.dart:16:13: Context: Unhandled core exception: RangeError: Index out of range: index must not be negative: -1
27+
// return str[-1];
28+
// ^
29+
//
30+
// pkg/front_end/testcases/const_functions/const_functions_string_error.dart:19:14: Error: Constant evaluation error:
31+
// const var4 = fn2();
32+
// ^
33+
// pkg/front_end/testcases/const_functions/const_functions_string_error.dart:22:13: Context: Unhandled core exception: RangeError: Index out of range: index should be less than 3: 3
34+
// return str[3];
35+
// ^
36+
//
37+
import self as self;
38+
import "dart:core" as core;
39+
40+
import "package:expect/expect.dart";
41+
42+
static const field core::String str = #C1;
43+
static const field core::String var1 = invalid-expression "Unhandled core exception: RangeError: Index out of range: index must not be negative: -1";
44+
static const field core::String var2 = invalid-expression "Unhandled core exception: RangeError: Index out of range: index should be less than 3: 3";
45+
static const field dynamic var3 = invalid-expression "Unhandled core exception: RangeError: Index out of range: index must not be negative: -1";
46+
static const field dynamic var4 = invalid-expression "Unhandled core exception: RangeError: Index out of range: index should be less than 3: 3";
47+
static const field dynamic var5 = invalid-expression "pkg/front_end/testcases/const_functions/const_functions_string_error.dart:28:14: Error: A value of type 'double' can't be assigned to a variable of type 'int'.
48+
return str[1.1];
49+
^";
50+
static method fn() → dynamic {
51+
core::String s = "str";
52+
return (#C1).{core::String::[]}(1.{core::int::unary-}());
53+
}
54+
static method fn2() → dynamic {
55+
core::String s = "str";
56+
return (#C1).{core::String::[]}(3);
57+
}
58+
static method fn3() → dynamic {
59+
core::String s = "str";
60+
return (#C1).{core::String::[]}(let final Never #t1 = invalid-expression "pkg/front_end/testcases/const_functions/const_functions_string_error.dart:28:14: Error: A value of type 'double' can't be assigned to a variable of type 'int'.
61+
return str[1.1];
62+
^" in 1.1 as{TypeError,ForNonNullableByDefault} core::int);
63+
}
64+
static method main() → void {}
65+
66+
constants {
67+
#C1 = "str"
68+
}

0 commit comments

Comments
 (0)