Skip to content

Commit b8dc762

Browse files
committed
[cfe] Handle NullCheck in constant evaluation
Closes #42802 Closes #42803 Change-Id: I282539cbfef90309dd343950101aefe0820785b7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/155600 Reviewed-by: Johnni Winther <[email protected]>
1 parent 40fd1c4 commit b8dc762

15 files changed

+325
-5
lines changed

pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,14 @@ Message _withArgumentsConstEvalNonConstantVariableGet(String string) {
13081308
arguments: {'string': string});
13091309
}
13101310

1311+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1312+
const Code<Null> codeConstEvalNonNull = messageConstEvalNonNull;
1313+
1314+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
1315+
const MessageCode messageConstEvalNonNull = const MessageCode(
1316+
"ConstEvalNonNull",
1317+
message: r"""Constant expression must be non-null.""");
1318+
13111319
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
13121320
const Code<Null> codeConstEvalNotListOrSetInSpread =
13131321
messageConstEvalNotListOrSetInSpread;

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import '../fasta_codes.dart'
4242
messageConstEvalFailedAssertion,
4343
messageConstEvalNotListOrSetInSpread,
4444
messageConstEvalNotMapInSpread,
45+
messageConstEvalNonNull,
4546
messageConstEvalNullValue,
4647
messageConstEvalStartingPoint,
4748
messageConstEvalUnevaluated,
@@ -2238,6 +2239,18 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
22382239
isNonNullableByDefault));
22392240
}
22402241

2242+
@override
2243+
Constant visitNullCheck(NullCheck node) {
2244+
final Constant constant = _evaluateSubexpression(node.operand);
2245+
if (constant is NullConstant) {
2246+
return report(node, messageConstEvalNonNull);
2247+
}
2248+
if (shouldBeUnevaluated) {
2249+
return unevaluated(node, new NullCheck(extract(constant)));
2250+
}
2251+
return constant;
2252+
}
2253+
22412254
@override
22422255
Constant visitSymbolLiteral(SymbolLiteral node) {
22432256
final Reference libraryReference =

pkg/front_end/messages.status

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ ConstEvalNegativeShift/example: Fail
122122
ConstEvalNonConstantVariableGet/example: Fail
123123
ConstEvalNotListOrSetInSpread/example: Fail
124124
ConstEvalNotMapInSpread/example: Fail
125+
ConstEvalNonNull/analyzerCode: Fail
126+
ConstEvalNonNull/example: Fail
125127
ConstEvalNullValue/example: Fail
126128
ConstEvalStartingPoint/analyzerCode: Fail # This is just used for displaying the starting point.
127129
ConstEvalStartingPoint/example: Fail # This is just used for displaying the starting point.

pkg/front_end/messages.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ ConstEvalNegativeShift:
132132
ConstEvalTruncateError:
133133
template: "Binary operator '#string ~/ #string2' results is Infinity or NaN."
134134

135+
ConstEvalNonNull:
136+
template: "Constant expression must be non-null."
137+
135138
ConstEvalInvalidMethodInvocation:
136139
template: "The method '#string' can't be invoked on '#constant' in a constant expression."
137140
analyzerCode: UNDEFINED_OPERATOR

pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_nnbd.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ worlds:
6464
{
6565
"name": "flutter",
6666
"rootUri": "../flutter",
67-
"packageUri": "lib/",
68-
"languageVersion": "2.9"
67+
"packageUri": "lib/"
6968
}
7069
]
7170
}

pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transform_nnbd.yaml.world.2.expect

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@ library from "org-dartlang-test:///main.dart" as main {
4747

4848
import "org-dartlang-test:///foo.dart";
4949

50-
static field foo::Foo foo = invalid-expression "Constant evaluation has no support for NullCheck!";
50+
static field foo::Foo foo = #C7;
5151
}
5252
constants {
5353
#C1 = null
54+
#C2 = "org-dartlang-test:///main.dart"
55+
#C3 = 3.0
56+
#C4 = 17.0
57+
#C5 = <wid::_Location*>[]
58+
#C6 = wid::_Location {file:#C2, line:#C3, column:#C4, name:#C1, parameterLocations:#C5}
59+
#C7 = foo::Foo {_location:#C6}
5460
}

pkg/front_end/testcases/incremental_initialize_from_dill/flutter_widget_transformer_non_const.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ worlds:
5959
{
6060
"name": "flutter",
6161
"rootUri": "../flutter",
62-
"packageUri": "lib/",
63-
"languageVersion": "2.9"
62+
"packageUri": "lib/"
6463
}
6564
]
6665
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) 2020, 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+
const int? a = 42;
6+
const int b = a!;
7+
8+
const int? c = null;
9+
const int? d = c!;
10+
11+
class Class {
12+
final int y;
13+
const Class(int? x) : y = x!;
14+
}
15+
16+
const Class e = const Class(a);
17+
const Class f = const Class(c);
18+
19+
main() {
20+
expect(42, a);
21+
expect(42, b);
22+
expect(42, e.y);
23+
}
24+
25+
expect(expected, actual) {
26+
if (expected != actual) throw 'Expected $expected, actual $actual';
27+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
library /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class Class extends core::Object /*hasConstConstructor*/ {
6+
final field core::int y;
7+
const constructor •(core::int? x) → self::Class
8+
: self::Class::y = x!, super core::Object::•()
9+
;
10+
}
11+
static const field core::int? a = 42;
12+
static const field core::int b = self::a!;
13+
static const field core::int? c = null;
14+
static const field core::int? d = self::c!;
15+
static const field self::Class e = const self::Class::•(self::a);
16+
static const field self::Class f = const self::Class::•(self::c);
17+
static method main() → dynamic
18+
;
19+
static method expect(dynamic expected, dynamic actual) → dynamic
20+
;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// Problems in component:
3+
//
4+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:17: Error: Constant evaluation error:
5+
// const int? d = c!;
6+
// ^
7+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:17: Context: Constant expression must be non-null.
8+
// const int? d = c!;
9+
// ^
10+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:12: Context: While analyzing:
11+
// const int? d = c!;
12+
// ^
13+
//
14+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:17:23: Error: Constant evaluation error:
15+
// const Class f = const Class(c);
16+
// ^
17+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:13:30: Context: Constant expression must be non-null.
18+
// const Class(int? x) : y = x!;
19+
// ^
20+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:17:13: Context: While analyzing:
21+
// const Class f = const Class(c);
22+
// ^
23+
//
24+
library /*isNonNullableByDefault*/;
25+
import self as self;
26+
import "dart:core" as core;
27+
28+
class Class extends core::Object /*hasConstConstructor*/ {
29+
final field core::int y;
30+
const constructor •(core::int? x) → self::Class
31+
: self::Class::y = x!, super core::Object::•()
32+
;
33+
}
34+
static const field core::int? a = #C1;
35+
static const field core::int b = #C1;
36+
static const field core::int? c = #C2;
37+
static const field core::int? d = invalid-expression "Constant expression must be non-null.";
38+
static const field self::Class e = #C3;
39+
static const field self::Class f = invalid-expression "Constant expression must be non-null.";
40+
static method main() → dynamic {
41+
self::expect(42, #C1);
42+
self::expect(42, #C1);
43+
self::expect(42, (#C3).{self::Class::y});
44+
}
45+
static method expect(dynamic expected, dynamic actual) → dynamic {
46+
if(!expected.{core::Object::==}(actual))
47+
throw "Expected ${expected}, actual ${actual}";
48+
}
49+
50+
constants {
51+
#C1 = 42
52+
#C2 = null
53+
#C3 = self::Class {y:#C1}
54+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// Problems in component:
3+
//
4+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:17: Error: Constant evaluation error:
5+
// const int? d = c!;
6+
// ^
7+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:17: Context: Constant expression must be non-null.
8+
// const int? d = c!;
9+
// ^
10+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:12: Context: While analyzing:
11+
// const int? d = c!;
12+
// ^
13+
//
14+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:17:23: Error: Constant evaluation error:
15+
// const Class f = const Class(c);
16+
// ^
17+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:13:30: Context: Constant expression must be non-null.
18+
// const Class(int? x) : y = x!;
19+
// ^
20+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:17:13: Context: While analyzing:
21+
// const Class f = const Class(c);
22+
// ^
23+
//
24+
library /*isNonNullableByDefault*/;
25+
import self as self;
26+
import "dart:core" as core;
27+
28+
class Class extends core::Object /*hasConstConstructor*/ {
29+
final field core::int y;
30+
const constructor •(core::int? x) → self::Class
31+
: self::Class::y = x!, super core::Object::•()
32+
;
33+
}
34+
static const field core::int? a = #C1;
35+
static const field core::int b = #C1;
36+
static const field core::int? c = #C2;
37+
static const field core::int? d = invalid-expression "Constant expression must be non-null.";
38+
static const field self::Class e = #C3;
39+
static const field self::Class f = invalid-expression "Constant expression must be non-null.";
40+
static method main() → dynamic {
41+
self::expect(42, #C1);
42+
self::expect(42, #C1);
43+
self::expect(42, (#C3).{self::Class::y});
44+
}
45+
static method expect(dynamic expected, dynamic actual) → dynamic {
46+
if(!expected.{core::Object::==}(actual))
47+
throw "Expected ${expected}, actual ${actual}";
48+
}
49+
50+
constants {
51+
#C1 = 42
52+
#C2 = null
53+
#C3 = self::Class {y:#C1}
54+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const int? a = 42;
2+
const int b = a!;
3+
const int? c = null;
4+
const int? d = c!;
5+
6+
class Class {
7+
final int y;
8+
const Class(int? x) : y = x!;
9+
}
10+
11+
const Class e = const Class(a);
12+
const Class f = const Class(c);
13+
main() {}
14+
expect(expected, actual) {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class Class {
2+
const Class(int? x) : y = x!;
3+
final int y;
4+
}
5+
6+
const Class e = const Class(a);
7+
const Class f = const Class(c);
8+
const int b = a!;
9+
const int? a = 42;
10+
const int? c = null;
11+
const int? d = c!;
12+
expect(expected, actual) {}
13+
main() {}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// Problems in component:
3+
//
4+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:17: Error: Constant evaluation error:
5+
// const int? d = c!;
6+
// ^
7+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:17: Context: Constant expression must be non-null.
8+
// const int? d = c!;
9+
// ^
10+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:12: Context: While analyzing:
11+
// const int? d = c!;
12+
// ^
13+
//
14+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:17:23: Error: Constant evaluation error:
15+
// const Class f = const Class(c);
16+
// ^
17+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:13:30: Context: Constant expression must be non-null.
18+
// const Class(int? x) : y = x!;
19+
// ^
20+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:17:13: Context: While analyzing:
21+
// const Class f = const Class(c);
22+
// ^
23+
//
24+
library /*isNonNullableByDefault*/;
25+
import self as self;
26+
import "dart:core" as core;
27+
28+
class Class extends core::Object /*hasConstConstructor*/ {
29+
final field core::int y;
30+
const constructor •(core::int? x) → self::Class
31+
: self::Class::y = x!, super core::Object::•()
32+
;
33+
}
34+
static const field core::int? a = #C1;
35+
static const field core::int b = #C1;
36+
static const field core::int? c = #C2;
37+
static const field core::int? d = invalid-expression "Constant expression must be non-null.";
38+
static const field self::Class e = #C3;
39+
static const field self::Class f = invalid-expression "Constant expression must be non-null.";
40+
static method main() → dynamic {
41+
self::expect(42, #C1);
42+
self::expect(42, #C1);
43+
self::expect(42, (#C3).{self::Class::y});
44+
}
45+
static method expect(dynamic expected, dynamic actual) → dynamic {
46+
if(!expected.{core::Object::==}(actual))
47+
throw "Expected ${expected}, actual ${actual}";
48+
}
49+
50+
constants {
51+
#C1 = 42
52+
#C2 = null
53+
#C3 = self::Class {y:#C1}
54+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// Problems in component:
3+
//
4+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:17: Error: Constant evaluation error:
5+
// const int? d = c!;
6+
// ^
7+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:17: Context: Constant expression must be non-null.
8+
// const int? d = c!;
9+
// ^
10+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:9:12: Context: While analyzing:
11+
// const int? d = c!;
12+
// ^
13+
//
14+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:17:23: Error: Constant evaluation error:
15+
// const Class f = const Class(c);
16+
// ^
17+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:13:30: Context: Constant expression must be non-null.
18+
// const Class(int? x) : y = x!;
19+
// ^
20+
// pkg/front_end/testcases/nnbd/constant_null_check.dart:17:13: Context: While analyzing:
21+
// const Class f = const Class(c);
22+
// ^
23+
//
24+
library /*isNonNullableByDefault*/;
25+
import self as self;
26+
import "dart:core" as core;
27+
28+
class Class extends core::Object /*hasConstConstructor*/ {
29+
final field core::int y;
30+
const constructor •(core::int? x) → self::Class
31+
: self::Class::y = x!, super core::Object::•()
32+
;
33+
}
34+
static const field core::int? a = #C1;
35+
static const field core::int b = #C1;
36+
static const field core::int? c = #C2;
37+
static const field core::int? d = invalid-expression "Constant expression must be non-null.";
38+
static const field self::Class e = #C3;
39+
static const field self::Class f = invalid-expression "Constant expression must be non-null.";
40+
static method main() → dynamic {
41+
self::expect(42, #C1);
42+
self::expect(42, #C1);
43+
self::expect(42, (#C3).{self::Class::y});
44+
}
45+
static method expect(dynamic expected, dynamic actual) → dynamic {
46+
if(!expected.{core::Object::==}(actual))
47+
throw "Expected ${expected}, actual ${actual}";
48+
}
49+
50+
constants {
51+
#C1 = 42
52+
#C2 = null
53+
#C3 = self::Class {y:#C1}
54+
}

0 commit comments

Comments
 (0)