Skip to content

Commit 90ec6f2

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
[cfe] Ensure that shorting is stopped before expression usage
The creation of the shorting stop expression was done lazily in NullAwareExpressionInferenceResult.expression. Since the shorting calls flow analysis to stop null promotion doing this out of order let to inconsistent flow analysis state. This CL introduces an `inferNullAwareExpression` the allows to caller to continue the shorting (if in an opt-in library). The inferExpression method, used everywhere else, stops any shorting currently in progress. Closes flutter#43275 Change-Id: I448163e29bdb0de8e493e5c62aa0474d0c9a593f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/173729 Reviewed-by: Dmitry Stefantsov <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent ce614d3 commit 90ec6f2

File tree

9 files changed

+254
-249
lines changed

9 files changed

+254
-249
lines changed

pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2949,11 +2949,8 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
29492949

29502950
@override
29512951
void ifNullExpression_end() {
2952-
// TODO(paulberry): CFE sometimes calls ifNullExpression_end and
2953-
// nullAwareAccess_end out of order, so as a workaround we cast to the
2954-
// common base class. See https://github.com/dart-lang/sdk/issues/43725.
2955-
_SimpleContext<Variable, Type> context =
2956-
_stack.removeLast() as _SimpleContext<Variable, Type>;
2952+
_IfNullExpressionContext<Variable, Type> context =
2953+
_stack.removeLast() as _IfNullExpressionContext<Variable, Type>;
29572954
_current = _merge(_current, context._previous);
29582955
}
29592956

@@ -3123,11 +3120,8 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
31233120

31243121
@override
31253122
void nullAwareAccess_end() {
3126-
// TODO(paulberry): CFE sometimes calls ifNullExpression_end and
3127-
// nullAwareAccess_end out of order, so as a workaround we cast to the
3128-
// common base class. See https://github.com/dart-lang/sdk/issues/43725.
3129-
_SimpleContext<Variable, Type> context =
3130-
_stack.removeLast() as _SimpleContext<Variable, Type>;
3123+
_NullAwareAccessContext<Variable, Type> context =
3124+
_stack.removeLast() as _NullAwareAccessContext<Variable, Type>;
31313125
_current = _merge(_current, context._previous);
31323126
}
31333127

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

Lines changed: 91 additions & 191 deletions
Large diffs are not rendered by default.

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,6 +1937,17 @@ class CompoundPropertySet extends InternalExpression {
19371937
String toString() {
19381938
return "CompoundPropertySet(${toStringInternal()})";
19391939
}
1940+
1941+
@override
1942+
void toTextInternal(AstPrinter printer) {
1943+
printer.writeExpression(receiver);
1944+
printer.write('.');
1945+
printer.writeName(propertyName);
1946+
printer.write(' ');
1947+
printer.writeName(binaryName);
1948+
printer.write('= ');
1949+
printer.writeExpression(rhs);
1950+
}
19401951
}
19411952

19421953
/// Internal expression representing an compound property assignment.
@@ -2939,6 +2950,28 @@ class NullAwareCompoundSet extends InternalExpression {
29392950
String toString() {
29402951
return "NullAwareCompoundSet(${toStringInternal()})";
29412952
}
2953+
2954+
@override
2955+
void toTextInternal(AstPrinter printer) {
2956+
printer.writeExpression(receiver);
2957+
printer.write('?.');
2958+
printer.writeName(propertyName);
2959+
if (forPostIncDec &&
2960+
rhs is IntLiteral &&
2961+
(rhs as IntLiteral).value == 1 &&
2962+
(binaryName == plusName || binaryName == minusName)) {
2963+
if (binaryName == plusName) {
2964+
printer.write('++');
2965+
} else {
2966+
printer.write('--');
2967+
}
2968+
} else {
2969+
printer.write(' ');
2970+
printer.writeName(binaryName);
2971+
printer.write('= ');
2972+
printer.writeExpression(rhs);
2973+
}
2974+
}
29422975
}
29432976

29442977
/// Internal expression representing an null-aware if-null property set.
@@ -3026,6 +3059,15 @@ class NullAwareIfNullSet extends InternalExpression {
30263059
String toString() {
30273060
return "NullAwareIfNullSet(${toStringInternal()})";
30283061
}
3062+
3063+
@override
3064+
void toTextInternal(AstPrinter printer) {
3065+
printer.writeExpression(receiver);
3066+
printer.write('?.');
3067+
printer.writeName(name);
3068+
printer.write(' ??= ');
3069+
printer.writeExpression(value);
3070+
}
30293071
}
30303072

30313073
/// Internal expression representing a compound super index assignment.

pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,17 +1644,13 @@ class TypeInferrerImpl implements TypeInferrer {
16441644
VariableDeclarationImpl variable) {
16451645
assert(variable.isImplicitlyTyped);
16461646
assert(variable.initializer != null);
1647-
ExpressionInferenceResult result = inferExpression(
1647+
ExpressionInferenceResult result = inferNullAwareExpression(
16481648
variable.initializer, const UnknownType(), true,
16491649
isVoidAllowed: true);
1650-
Link<NullAwareGuard> nullAwareGuards;
1651-
if (isNonNullableByDefault) {
1652-
variable.initializer = result.nullAwareAction..parent = variable;
1653-
nullAwareGuards = result.nullAwareGuards;
1654-
} else {
1655-
variable.initializer = result.expression..parent = variable;
1656-
nullAwareGuards = const Link<NullAwareGuard>();
1657-
}
1650+
1651+
Link<NullAwareGuard> nullAwareGuards = result.nullAwareGuards;
1652+
variable.initializer = result.nullAwareAction..parent = variable;
1653+
16581654
DartType inferredType =
16591655
inferDeclarationType(result.inferredType, forSyntheticVariable: true);
16601656
instrumentation?.record(uriForInstrumentation, variable.fileOffset, 'type',
@@ -1713,7 +1709,7 @@ class TypeInferrerImpl implements TypeInferrer {
17131709
///
17141710
/// Derived classes should override this method with logic that dispatches on
17151711
/// the expression type and calls the appropriate specialized "infer" method.
1716-
ExpressionInferenceResult inferExpression(
1712+
ExpressionInferenceResult _inferExpression(
17171713
Expression expression, DartType typeContext, bool typeNeeded,
17181714
{bool isVoidAllowed: false, bool forEffect: false}) {
17191715
registerIfUnreachableForTesting(expression);
@@ -1768,6 +1764,28 @@ class TypeInferrerImpl implements TypeInferrer {
17681764
return result;
17691765
}
17701766

1767+
ExpressionInferenceResult inferExpression(
1768+
Expression expression, DartType typeContext, bool typeNeeded,
1769+
{bool isVoidAllowed: false, bool forEffect: false}) {
1770+
ExpressionInferenceResult result = _inferExpression(
1771+
expression, typeContext, typeNeeded,
1772+
isVoidAllowed: isVoidAllowed, forEffect: forEffect);
1773+
return result.stopShorting();
1774+
}
1775+
1776+
ExpressionInferenceResult inferNullAwareExpression(
1777+
Expression expression, DartType typeContext, bool typeNeeded,
1778+
{bool isVoidAllowed: false, bool forEffect: false}) {
1779+
ExpressionInferenceResult result = _inferExpression(
1780+
expression, typeContext, typeNeeded,
1781+
isVoidAllowed: isVoidAllowed, forEffect: forEffect);
1782+
if (isNonNullableByDefault) {
1783+
return result;
1784+
} else {
1785+
return result.stopShorting();
1786+
}
1787+
}
1788+
17711789
@override
17721790
Expression inferFieldInitializer(
17731791
InferenceHelper helper,
@@ -3965,6 +3983,8 @@ class ExpressionInferenceResult {
39653983

39663984
DartType get nullAwareActionType => inferredType;
39673985

3986+
ExpressionInferenceResult stopShorting() => this;
3987+
39683988
String toString() => 'ExpressionInferenceResult($inferredType,$expression)';
39693989
}
39703990

@@ -4045,24 +4065,26 @@ class NullAwareExpressionInferenceResult implements ExpressionInferenceResult {
40454065
@override
40464066
final Expression nullAwareAction;
40474067

4048-
Expression _expression;
4049-
40504068
NullAwareExpressionInferenceResult(this.inferredType,
40514069
this.nullAwareActionType, this.nullAwareGuards, this.nullAwareAction)
40524070
: assert(nullAwareGuards.isNotEmpty),
40534071
assert(nullAwareAction != null);
40544072

40554073
Expression get expression {
4056-
if (_expression == null) {
4057-
_expression = nullAwareAction;
4058-
Link<NullAwareGuard> nullAwareGuard = nullAwareGuards;
4059-
while (nullAwareGuard.isNotEmpty) {
4060-
_expression =
4061-
nullAwareGuard.head.createExpression(inferredType, _expression);
4062-
nullAwareGuard = nullAwareGuard.tail;
4063-
}
4074+
throw new UnsupportedError('Shorting must be explicitly stopped before'
4075+
'accessing the expression result of a '
4076+
'NullAwareExpressionInferenceResult');
4077+
}
4078+
4079+
ExpressionInferenceResult stopShorting() {
4080+
Expression expression = nullAwareAction;
4081+
Link<NullAwareGuard> nullAwareGuard = nullAwareGuards;
4082+
while (nullAwareGuard.isNotEmpty) {
4083+
expression =
4084+
nullAwareGuard.head.createExpression(inferredType, expression);
4085+
nullAwareGuard = nullAwareGuard.tail;
40644086
}
4065-
return _expression;
4087+
return new ExpressionInferenceResult(inferredType, expression);
40664088
}
40674089

40684090
String toString() =>

pkg/front_end/test/spell_checking_list_code.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,7 @@ stdlib
10621062
stdout
10631063
sticky
10641064
stmt
1065+
stopped
10651066
str
10661067
strategies
10671068
streak

pkg/front_end/test/text_representation/internal_ast_text_representation_test.dart

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,18 @@ foo ?? foo = 1''');
581581

582582
void _testCompoundExtensionSet() {}
583583

584-
void _testCompoundPropertySet() {}
584+
void _testCompoundPropertySet() {
585+
testExpression(
586+
new CompoundPropertySet(
587+
new IntLiteral(0), new Name('foo'), new Name('+'), new IntLiteral(1),
588+
readOffset: TreeNode.noOffset,
589+
binaryOffset: TreeNode.noOffset,
590+
writeOffset: TreeNode.noOffset,
591+
readOnlyReceiver: false,
592+
forEffect: false),
593+
'''
594+
0.foo += 1''');
595+
}
585596

586597
void _testPropertyPostIncDec() {}
587598

@@ -607,9 +618,40 @@ void _testIfNullExtensionIndexSet() {}
607618

608619
void _testCompoundIndexSet() {}
609620

610-
void _testNullAwareCompoundSet() {}
621+
void _testNullAwareCompoundSet() {
622+
testExpression(
623+
new NullAwareCompoundSet(
624+
new IntLiteral(0), new Name('foo'), new Name('+'), new IntLiteral(1),
625+
readOffset: TreeNode.noOffset,
626+
binaryOffset: TreeNode.noOffset,
627+
writeOffset: TreeNode.noOffset,
628+
forPostIncDec: false,
629+
forEffect: false),
630+
'''
631+
0?.foo += 1''');
632+
testExpression(
633+
new NullAwareCompoundSet(
634+
new IntLiteral(0), new Name('foo'), new Name('+'), new IntLiteral(1),
635+
readOffset: TreeNode.noOffset,
636+
binaryOffset: TreeNode.noOffset,
637+
writeOffset: TreeNode.noOffset,
638+
forPostIncDec: true,
639+
forEffect: false),
640+
'''
641+
0?.foo++''');
642+
}
611643

612-
void _testNullAwareIfNullSet() {}
644+
void _testNullAwareIfNullSet() {
645+
testExpression(
646+
new NullAwareIfNullSet(
647+
new IntLiteral(0), new Name('foo'), new IntLiteral(1),
648+
readOffset: TreeNode.noOffset,
649+
testOffset: TreeNode.noOffset,
650+
writeOffset: TreeNode.noOffset,
651+
forEffect: false),
652+
'''
653+
0?.foo ??= 1''');
654+
}
613655

614656
void _testCompoundSuperIndexSet() {}
615657

pkg/front_end/testcases/general/issue43290.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
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+
15
class Class {
26
final int length;
37

pkg/front_end/testcases/general/issue43290.dart.strong.expect

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,45 @@ library;
22
//
33
// Problems in library:
44
//
5-
// pkg/front_end/testcases/general/issue43290.dart:11:25: Error: Not a constant expression.
5+
// pkg/front_end/testcases/general/issue43290.dart:15:25: Error: Not a constant expression.
66
// const Class(length: length);
77
// ^^^^^^
88
//
9-
// pkg/front_end/testcases/general/issue43290.dart:19:15: Error: Not a constant expression.
9+
// pkg/front_end/testcases/general/issue43290.dart:23:15: Error: Not a constant expression.
1010
// const a = length;
1111
// ^^^^^^
1212
//
13-
// pkg/front_end/testcases/general/issue43290.dart:7:11: Error: Constant evaluation error:
13+
// pkg/front_end/testcases/general/issue43290.dart:11:11: Error: Constant evaluation error:
1414
// const Class(length: this.length);
1515
// ^
16-
// pkg/front_end/testcases/general/issue43290.dart:7:30: Context: Not a constant expression.
16+
// pkg/front_end/testcases/general/issue43290.dart:11:30: Context: Not a constant expression.
1717
// const Class(length: this.length);
1818
// ^
1919
//
20-
// pkg/front_end/testcases/general/issue43290.dart:11:11: Error: Constant evaluation error:
20+
// pkg/front_end/testcases/general/issue43290.dart:15:11: Error: Constant evaluation error:
2121
// const Class(length: length);
2222
// ^
23-
// pkg/front_end/testcases/general/issue43290.dart:11:25: Context: Not a constant expression.
23+
// pkg/front_end/testcases/general/issue43290.dart:15:25: Context: Not a constant expression.
2424
// const Class(length: length);
2525
// ^
2626
//
27-
// pkg/front_end/testcases/general/issue43290.dart:15:20: Error: Constant evaluation error:
27+
// pkg/front_end/testcases/general/issue43290.dart:19:20: Error: Constant evaluation error:
2828
// const a = this.length;
2929
// ^
30-
// pkg/front_end/testcases/general/issue43290.dart:15:20: Context: Not a constant expression.
30+
// pkg/front_end/testcases/general/issue43290.dart:19:20: Context: Not a constant expression.
3131
// const a = this.length;
3232
// ^
33-
// pkg/front_end/testcases/general/issue43290.dart:15:11: Context: While analyzing:
33+
// pkg/front_end/testcases/general/issue43290.dart:19:11: Context: While analyzing:
3434
// const a = this.length;
3535
// ^
3636
//
37-
// pkg/front_end/testcases/general/issue43290.dart:19:15: Error: Constant evaluation error:
37+
// pkg/front_end/testcases/general/issue43290.dart:23:15: Error: Constant evaluation error:
3838
// const a = length;
3939
// ^
40-
// pkg/front_end/testcases/general/issue43290.dart:19:15: Context: Not a constant expression.
40+
// pkg/front_end/testcases/general/issue43290.dart:23:15: Context: Not a constant expression.
4141
// const a = length;
4242
// ^
43-
// pkg/front_end/testcases/general/issue43290.dart:19:11: Context: While analyzing:
43+
// pkg/front_end/testcases/general/issue43290.dart:23:11: Context: While analyzing:
4444
// const a = length;
4545
// ^
4646
//

pkg/front_end/testcases/general/issue43290.dart.strong.transformed.expect

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,45 @@ library;
22
//
33
// Problems in library:
44
//
5-
// pkg/front_end/testcases/general/issue43290.dart:11:25: Error: Not a constant expression.
5+
// pkg/front_end/testcases/general/issue43290.dart:15:25: Error: Not a constant expression.
66
// const Class(length: length);
77
// ^^^^^^
88
//
9-
// pkg/front_end/testcases/general/issue43290.dart:19:15: Error: Not a constant expression.
9+
// pkg/front_end/testcases/general/issue43290.dart:23:15: Error: Not a constant expression.
1010
// const a = length;
1111
// ^^^^^^
1212
//
13-
// pkg/front_end/testcases/general/issue43290.dart:7:11: Error: Constant evaluation error:
13+
// pkg/front_end/testcases/general/issue43290.dart:11:11: Error: Constant evaluation error:
1414
// const Class(length: this.length);
1515
// ^
16-
// pkg/front_end/testcases/general/issue43290.dart:7:30: Context: Not a constant expression.
16+
// pkg/front_end/testcases/general/issue43290.dart:11:30: Context: Not a constant expression.
1717
// const Class(length: this.length);
1818
// ^
1919
//
20-
// pkg/front_end/testcases/general/issue43290.dart:11:11: Error: Constant evaluation error:
20+
// pkg/front_end/testcases/general/issue43290.dart:15:11: Error: Constant evaluation error:
2121
// const Class(length: length);
2222
// ^
23-
// pkg/front_end/testcases/general/issue43290.dart:11:25: Context: Not a constant expression.
23+
// pkg/front_end/testcases/general/issue43290.dart:15:25: Context: Not a constant expression.
2424
// const Class(length: length);
2525
// ^
2626
//
27-
// pkg/front_end/testcases/general/issue43290.dart:15:20: Error: Constant evaluation error:
27+
// pkg/front_end/testcases/general/issue43290.dart:19:20: Error: Constant evaluation error:
2828
// const a = this.length;
2929
// ^
30-
// pkg/front_end/testcases/general/issue43290.dart:15:20: Context: Not a constant expression.
30+
// pkg/front_end/testcases/general/issue43290.dart:19:20: Context: Not a constant expression.
3131
// const a = this.length;
3232
// ^
33-
// pkg/front_end/testcases/general/issue43290.dart:15:11: Context: While analyzing:
33+
// pkg/front_end/testcases/general/issue43290.dart:19:11: Context: While analyzing:
3434
// const a = this.length;
3535
// ^
3636
//
37-
// pkg/front_end/testcases/general/issue43290.dart:19:15: Error: Constant evaluation error:
37+
// pkg/front_end/testcases/general/issue43290.dart:23:15: Error: Constant evaluation error:
3838
// const a = length;
3939
// ^
40-
// pkg/front_end/testcases/general/issue43290.dart:19:15: Context: Not a constant expression.
40+
// pkg/front_end/testcases/general/issue43290.dart:23:15: Context: Not a constant expression.
4141
// const a = length;
4242
// ^
43-
// pkg/front_end/testcases/general/issue43290.dart:19:11: Context: While analyzing:
43+
// pkg/front_end/testcases/general/issue43290.dart:23:11: Context: While analyzing:
4444
// const a = length;
4545
// ^
4646
//

0 commit comments

Comments
 (0)