Skip to content

Commit e3443b9

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Flow analysis: implement "why not promoted" for binary/unary operator target.
Bug: #44898 Change-Id: If464a54bdb63fc661db312df5dc10f108049286a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/196240 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent 6645753 commit e3443b9

File tree

3 files changed

+106
-13
lines changed

3 files changed

+106
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
// This test contains a test case for each condition that can lead to the front
6+
// end's `NullableOperatorCallError` error, for which we wish to report "why not
7+
// promoted" context information.
8+
9+
class C1 {
10+
int? bad;
11+
}
12+
13+
userDefinableBinaryOpLhs(C1 c) {
14+
if (c.bad == null) return;
15+
c.bad
16+
/*cfe.invoke: notPromoted(propertyNotPromoted(target: member:C1.bad, type: int?))*/
17+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: int?))*/
18+
+
19+
1;
20+
}
21+
22+
class C2 {
23+
int? bad;
24+
}
25+
26+
userDefinableUnaryOp(C2 c) {
27+
if (c.bad == null) return;
28+
/*cfe.invoke: notPromoted(propertyNotPromoted(target: member:C2.bad, type: int?))*/
29+
-c.
30+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C2.bad, type: int?))*/
31+
bad;
32+
}

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

+37-13
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,8 @@ class InferenceVisitor
666666
read,
667667
readType,
668668
node.binaryName,
669-
node.rhs);
669+
node.rhs,
670+
null);
670671

671672
Expression binary = binaryResult.expression;
672673
DartType binaryType = binaryResult.inferredType;
@@ -2906,7 +2907,8 @@ class InferenceVisitor
29062907
read,
29072908
readType,
29082909
node.binaryName,
2909-
node.rhs);
2910+
node.rhs,
2911+
null);
29102912
DartType binaryType = binaryResult.inferredType;
29112913

29122914
Expression binary =
@@ -3999,7 +4001,8 @@ class InferenceVisitor
39994001
Expression left,
40004002
DartType leftType,
40014003
Name binaryName,
4002-
Expression right) {
4004+
Expression right,
4005+
Map<DartType, NonPromotionReason> Function() whyNotPromoted) {
40034006
assert(binaryName != equalsName);
40044007

40054008
ObjectAccessTarget binaryTarget = inferrer.findInterfaceMember(
@@ -4203,14 +4206,19 @@ class InferenceVisitor
42034206
}
42044207

42054208
if (!inferrer.isTopLevel && binaryTarget.isNullable) {
4209+
List<LocatedMessage> context = inferrer.getWhyNotPromotedContext(
4210+
whyNotPromoted?.call(),
4211+
binary,
4212+
(type) => !type.isPotentiallyNullable);
42064213
return new ExpressionInferenceResult(
42074214
binaryType,
42084215
inferrer.helper.wrapInProblem(
42094216
binary,
42104217
templateNullableOperatorCallError.withArguments(
42114218
binaryName.text, leftType, inferrer.isNonNullableByDefault),
42124219
binary.fileOffset,
4213-
binaryName.text.length));
4220+
binaryName.text.length,
4221+
context: context));
42144222
}
42154223
return new ExpressionInferenceResult(binaryType, binary);
42164224
}
@@ -4220,8 +4228,12 @@ class InferenceVisitor
42204228
///
42214229
/// [fileOffset] is used as the file offset for created nodes.
42224230
/// [expressionType] is the already inferred type of the [expression].
4223-
ExpressionInferenceResult _computeUnaryExpression(int fileOffset,
4224-
Expression expression, DartType expressionType, Name unaryName) {
4231+
ExpressionInferenceResult _computeUnaryExpression(
4232+
int fileOffset,
4233+
Expression expression,
4234+
DartType expressionType,
4235+
Name unaryName,
4236+
Map<DartType, NonPromotionReason> Function() whyNotPromoted) {
42254237
ObjectAccessTarget unaryTarget = inferrer.findInterfaceMember(
42264238
expressionType, unaryName, fileOffset,
42274239
includeExtensionMethods: true);
@@ -4352,6 +4364,8 @@ class InferenceVisitor
43524364
}
43534365

43544366
if (!inferrer.isTopLevel && unaryTarget.isNullable) {
4367+
List<LocatedMessage> context = inferrer.getWhyNotPromotedContext(
4368+
whyNotPromoted?.call(), unary, (type) => !type.isPotentiallyNullable);
43554369
// TODO(johnniwinther): Special case 'unary-' in messages. It should
43564370
// probably be referred to as "Unary operator '-' ...".
43574371
return new ExpressionInferenceResult(
@@ -4361,7 +4375,8 @@ class InferenceVisitor
43614375
templateNullableOperatorCallError.withArguments(unaryName.text,
43624376
expressionType, inferrer.isNonNullableByDefault),
43634377
unary.fileOffset,
4364-
unaryName == unaryMinusName ? 1 : unaryName.text.length));
4378+
unaryName == unaryMinusName ? 1 : unaryName.text.length,
4379+
context: context));
43654380
}
43664381
return new ExpressionInferenceResult(unaryType, unary);
43674382
}
@@ -5121,7 +5136,8 @@ class InferenceVisitor
51215136
left,
51225137
readType,
51235138
node.binaryName,
5124-
node.rhs);
5139+
node.rhs,
5140+
null);
51255141
Expression binary = binaryResult.expression;
51265142
DartType binaryType = binaryResult.inferredType;
51275143

@@ -5263,7 +5279,8 @@ class InferenceVisitor
52635279
left,
52645280
readType,
52655281
node.binaryName,
5266-
node.rhs);
5282+
node.rhs,
5283+
null);
52675284
Expression binary = binaryResult.expression;
52685285
DartType binaryType = binaryResult.inferredType;
52695286

@@ -5417,7 +5434,8 @@ class InferenceVisitor
54175434
left,
54185435
readType,
54195436
node.binaryName,
5420-
node.rhs);
5437+
node.rhs,
5438+
null);
54215439
Expression binary = binaryResult.expression;
54225440
DartType binaryType = binaryResult.inferredType;
54235441

@@ -5596,7 +5614,8 @@ class InferenceVisitor
55965614
left,
55975615
readType,
55985616
node.binaryName,
5599-
node.rhs);
5617+
node.rhs,
5618+
null);
56005619

56015620
Expression binary = binaryResult.expression;
56025621
DartType binaryType = binaryResult.inferredType;
@@ -6922,13 +6941,16 @@ class InferenceVisitor
69226941
BinaryExpression node, DartType typeContext) {
69236942
ExpressionInferenceResult leftResult =
69246943
inferrer.inferExpression(node.left, const UnknownType(), true);
6944+
Map<DartType, NonPromotionReason> Function() whyNotPromoted =
6945+
inferrer.flowAnalysis?.whyNotPromoted(leftResult.expression);
69256946
return _computeBinaryExpression(
69266947
node.fileOffset,
69276948
typeContext,
69286949
leftResult.expression,
69296950
leftResult.inferredType,
69306951
node.binaryName,
6931-
node.right);
6952+
node.right,
6953+
whyNotPromoted);
69326954
}
69336955

69346956
ExpressionInferenceResult visitUnary(
@@ -7001,8 +7023,10 @@ class InferenceVisitor
70017023
expressionResult =
70027024
inferrer.inferExpression(node.expression, const UnknownType(), true);
70037025
}
7026+
Map<DartType, NonPromotionReason> Function() whyNotPromoted =
7027+
inferrer.flowAnalysis?.whyNotPromoted(expressionResult.expression);
70047028
return _computeUnaryExpression(node.fileOffset, expressionResult.expression,
7005-
expressionResult.inferredType, node.unaryName);
7029+
expressionResult.inferredType, node.unaryName, whyNotPromoted);
70067030
}
70077031

70087032
ExpressionInferenceResult visitParenthesized(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
// This test contains a test case for each condition that can lead to the front
6+
// end's `NullableOperatorCallError` error, for which we wish to report "why not
7+
// promoted" context information.
8+
9+
class C1 {
10+
int? bad;
11+
// ^^^
12+
// [context 2] 'bad' refers to a property so it couldn't be promoted. See http://dart.dev/go/non-promo-property
13+
// [context 3] 'bad' refers to a property so it couldn't be promoted.
14+
}
15+
16+
userDefinableBinaryOpLhs(C1 c) {
17+
if (c.bad == null) return;
18+
c.bad + 1;
19+
// ^
20+
// [analyzer 2] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
21+
// [cfe 3] Operator '+' cannot be called on 'int?' because it is potentially null.
22+
}
23+
24+
class C2 {
25+
int? bad;
26+
// ^^^
27+
// [context 1] 'bad' refers to a property so it couldn't be promoted. See http://dart.dev/go/non-promo-property
28+
// [context 4] 'bad' refers to a property so it couldn't be promoted.
29+
}
30+
31+
userDefinableUnaryOp(C2 c) {
32+
if (c.bad == null) return;
33+
-c.bad;
34+
//^
35+
// [analyzer 1] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
36+
// [cfe 4] Operator 'unary-' cannot be called on 'int?' because it is potentially null.
37+
}

0 commit comments

Comments
 (0)