Skip to content

Commit c3ce683

Browse files
stereotype441Commit Queue
authored andcommitted
[Breaking change] Account for field promotion to Null when computing reachability based on equality tests.
Fixes #56893. Fixes dart-lang/language#4127. Bug: #56893 Bug: dart-lang/language#4127 Change-Id: If8bb0144ebe7024a7f4f7c1733e24632b4549c7f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/389660 Commit-Queue: Paul Berry <[email protected]> Reviewed-by: Kallen Tu <[email protected]>
1 parent 47d5502 commit c3ce683

File tree

4 files changed

+64
-67
lines changed

4 files changed

+64
-67
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ main() {
3131
}
3232
```
3333

34+
#### Other Language Changes
35+
36+
- **Breaking Change** [#56893][]: If a field is promoted to the type `Null`
37+
using `is` or `as`, this type promotion is now properly accounted for in
38+
reachability analysis. This makes the type system more self-consistent,
39+
because it mirrors the behavior of promoted local variables. This change is
40+
not expected to make any difference in practice.
41+
42+
[#56893]: https://github.com/dart-lang/sdk/issues/56893
43+
3444
#### Dart to Javascript Compiler (dart2js)
3545

3646
- The dart2js compiler which is invoked when the command

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5396,7 +5396,7 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
53965396
propertyMember: propertyMember,
53975397
promotionKey: propertySsaNode.promotionKey,
53985398
model: _current,
5399-
type: unpromotedType,
5399+
type: promotedType ?? unpromotedType,
54005400
ssaNode: propertySsaNode);
54015401
if (wholeExpression != null) {
54025402
_storeExpressionInfo(wholeExpression, propertyReference);
@@ -5418,7 +5418,7 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
54185418
propertyMember: propertyMember,
54195419
promotionKey: propertySsaNode.promotionKey,
54205420
model: _current,
5421-
type: unpromotedType,
5421+
type: promotedType ?? unpromotedType,
54225422
ssaNode: propertySsaNode);
54235423
_stack.add(new _PropertyPatternContext<Type>(
54245424
_makeTemporaryReference(

pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7408,16 +7408,10 @@ main() {
74087408
});
74097409

74107410
group('and equality:', () {
7411-
test('promoted type ignored on LHS', () {
7412-
// Normally flow analysis understands when an `if` test is guaranteed to
7413-
// succeed (or fail) based on the static types of the LHS and RHS. But
7414-
// due to https://github.com/dart-lang/language/issues/4127, this
7415-
// doesn't fully work when the LHS or RHS is a property reference; in
7416-
// that case, the unpromoted type is used.
7417-
7418-
// This test is here to make sure we don't change the existing behavior
7419-
// by accident; if/when we fix #4127, this test should be changed
7420-
// accordingly.
7411+
test('promoted type accounted for on LHS', () {
7412+
// Flow analysis understands when an `if` test is guaranteed to succeed
7413+
// (or fail) based on the static types of the LHS and RHS. Make sure
7414+
// this works when the LHS or RHS is a property reference.
74217415
h.addMember('C', 'f', 'Object?', promotable: true);
74227416
h.thisType = 'C';
74237417
h.run([
@@ -7426,21 +7420,15 @@ main() {
74267420
if_(thisProperty('f').eq(nullLiteral), [
74277421
checkReachable(true),
74287422
], [
7429-
checkReachable(true),
7423+
checkReachable(false),
74307424
]),
74317425
]);
74327426
});
74337427

7434-
test('promoted type ignored on RHS', () {
7435-
// Normally flow analysis understands when an `if` test is guaranteed to
7436-
// succeed (or fail) based on the static types of the LHS and RHS. But
7437-
// due to https://github.com/dart-lang/language/issues/4127, this
7438-
// doesn't fully work when the LHS or RHS is a property reference; in
7439-
// that case, the unpromoted type is used.
7440-
7441-
// This test is here to make sure we don't change the existing behavior
7442-
// by accident; if/when we fix #4127, this test should be changed
7443-
// accordingly.
7428+
test('promoted type accounted for on RHS', () {
7429+
// Flow analysis understands when an `if` test is guaranteed to succeed
7430+
// (or fail) based on the static types of the LHS and RHS. Make sure
7431+
// this works when the LHS or RHS is a property reference.
74447432
h.addMember('C', 'f', 'Object?', promotable: true);
74457433
h.thisType = 'C';
74467434
h.run([
@@ -7449,7 +7437,7 @@ main() {
74497437
if_(nullLiteral.eq(thisProperty('f')), [
74507438
checkReachable(true),
74517439
], [
7452-
checkReachable(true),
7440+
checkReachable(false),
74537441
]),
74547442
]);
74557443
});

tests/language/inference_update_2/promoted_field_type_in_equality_test.dart

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
// Tests the behavior described in
6-
// https://github.com/dart-lang/language/issues/4127, namely the fact that when
7-
// deciding whether an `==` or `!=` comparison is guaranteed to evaluate to
8-
// `true` or `false`, flow analysis considers promoted fields to have their base
9-
// type rather than their promoted type.
5+
// Tests that the behavior described in
6+
// https://github.com/dart-lang/language/issues/4127 has been fixed. That is,
7+
// when deciding whether an `==` or `!=` comparison is guaranteed to evaluate to
8+
// `true` or `false`, flow analysis considers promoted fields to have their
9+
// promoted type.
1010

11-
// This test is here to make sure we don't change the existing behavior by
12-
// accident; if/when we fix #4127, this test should be changed accordingly.
11+
// This test acts as a regression test for #4127.
1312

1413
import '../static_type_helper.dart';
1514

@@ -30,10 +29,10 @@ class C {
3029
y = null;
3130
}
3231
// In analyzing the `==` check, flow analysis assumes that `_f` has its
33-
// unpromoted type (`Object?`), so both branches of the `if` are
34-
// reachable. Therefore both `x` and `y` should both be demoted here.
32+
// promoted type (`Null`), so only the `x = null` branch is
33+
// reachable. Therefore only `x` should be demoted here.
3534
x.expectStaticType<Exactly<int?>>();
36-
y.expectStaticType<Exactly<int?>>();
35+
y.expectStaticType<Exactly<int>>();
3736
}
3837

3938
void testImplicitThisReferenceOnRhsOfEquals() {
@@ -49,10 +48,10 @@ class C {
4948
y = null;
5049
}
5150
// In analyzing the `==` check, flow analysis assumes that `_f` has its
52-
// unpromoted type (`Object?`), so both branches of the `if` are
53-
// reachable. Therefore both `x` and `y` should both be demoted here.
51+
// promoted type (`Null`), so only the `x = null` branch is
52+
// reachable. Therefore only `x` should be demoted here.
5453
x.expectStaticType<Exactly<int?>>();
55-
y.expectStaticType<Exactly<int?>>();
54+
y.expectStaticType<Exactly<int>>();
5655
}
5756

5857
void testImplicitThisReferenceOnLhsOfNotEquals() {
@@ -68,9 +67,9 @@ class C {
6867
y = null;
6968
}
7069
// In analyzing the `!=` check, flow analysis assumes that `_f` has its
71-
// unpromoted type (`Object?`), so both branches of the `if` are
72-
// reachable. Therefore both `x` and `y` should both be demoted here.
73-
x.expectStaticType<Exactly<int?>>();
70+
// promoted type (`Null`), so only the `y = null` branch is
71+
// reachable. Therefore only `y` should be demoted here.
72+
x.expectStaticType<Exactly<int>>();
7473
y.expectStaticType<Exactly<int?>>();
7574
}
7675

@@ -87,9 +86,9 @@ class C {
8786
y = null;
8887
}
8988
// In analyzing the `!=` check, flow analysis assumes that `_f` has its
90-
// unpromoted type (`Object?`), so both branches of the `if` are
91-
// reachable. Therefore both `x` and `y` should both be demoted here.
92-
x.expectStaticType<Exactly<int?>>();
89+
// promoted type (`Null`), so only the `y = null` branch is
90+
// reachable. Therefore only `y` should be demoted here.
91+
x.expectStaticType<Exactly<int>>();
9392
y.expectStaticType<Exactly<int?>>();
9493
}
9594

@@ -106,10 +105,10 @@ class C {
106105
y = null;
107106
}
108107
// In analyzing the `==` check, flow analysis assumes that `this._f` has its
109-
// unpromoted type (`Object?`), so both branches of the `if` are
110-
// reachable. Therefore both `x` and `y` should both be demoted here.
108+
// promoted type (`Null`), so only the `x = null` branch is
109+
// reachable. Therefore only `x` should be demoted here.
111110
x.expectStaticType<Exactly<int?>>();
112-
y.expectStaticType<Exactly<int?>>();
111+
y.expectStaticType<Exactly<int>>();
113112
}
114113

115114
void testExplicitThisReferenceOnRhsOfEquals() {
@@ -125,10 +124,10 @@ class C {
125124
y = null;
126125
}
127126
// In analyzing the `==` check, flow analysis assumes that `this._f` has its
128-
// unpromoted type (`Object?`), so both branches of the `if` are
129-
// reachable. Therefore both `x` and `y` should both be demoted here.
127+
// promoted type (`Null`), so only the `x = null` branch is
128+
// reachable. Therefore only `x` should be demoted here.
130129
x.expectStaticType<Exactly<int?>>();
131-
y.expectStaticType<Exactly<int?>>();
130+
y.expectStaticType<Exactly<int>>();
132131
}
133132

134133
void testExplicitThisReferenceOnLhsOfNotEquals() {
@@ -144,9 +143,9 @@ class C {
144143
y = null;
145144
}
146145
// In analyzing the `!=` check, flow analysis assumes that `this._f` has its
147-
// unpromoted type (`Object?`), so both branches of the `if` are
148-
// reachable. Therefore both `x` and `y` should both be demoted here.
149-
x.expectStaticType<Exactly<int?>>();
146+
// promoted type (`Null`), so only the `y = null` branch is
147+
// reachable. Therefore only `y` should be demoted here.
148+
x.expectStaticType<Exactly<int>>();
150149
y.expectStaticType<Exactly<int?>>();
151150
}
152151

@@ -163,9 +162,9 @@ class C {
163162
y = null;
164163
}
165164
// In analyzing the `!=` check, flow analysis assumes that `this._f` has its
166-
// unpromoted type (`Object?`), so both branches of the `if` are
167-
// reachable. Therefore both `x` and `y` should both be demoted here.
168-
x.expectStaticType<Exactly<int?>>();
165+
// promoted type (`Null`), so only the `y = null` branch is
166+
// reachable. Therefore only `y` should be demoted here.
167+
x.expectStaticType<Exactly<int>>();
169168
y.expectStaticType<Exactly<int?>>();
170169
}
171170
}
@@ -183,10 +182,10 @@ void testExplicitPropertyReferenceOnLhsOfEquals(C c) {
183182
y = null;
184183
}
185184
// In analyzing the `==` check, flow analysis assumes that `c._f` has its
186-
// unpromoted type (`Object?`), so both branches of the `if` are
187-
// reachable. Therefore both `x` and `y` should both be demoted here.
185+
// promoted type (`Null`), so only the `x = null` branch is
186+
// reachable. Therefore only `x` should be demoted here.
188187
x.expectStaticType<Exactly<int?>>();
189-
y.expectStaticType<Exactly<int?>>();
188+
y.expectStaticType<Exactly<int>>();
190189
}
191190

192191
void testExplicitPropertyReferenceOnRhsOfEquals(C c) {
@@ -202,10 +201,10 @@ void testExplicitPropertyReferenceOnRhsOfEquals(C c) {
202201
y = null;
203202
}
204203
// In analyzing the `==` check, flow analysis assumes that `c._f` has its
205-
// unpromoted type (`Object?`), so both branches of the `if` are
206-
// reachable. Therefore both `x` and `y` should both be demoted here.
204+
// promoted type (`Null`), so only the `x = null` branch is
205+
// reachable. Therefore only `x` should be demoted here.
207206
x.expectStaticType<Exactly<int?>>();
208-
y.expectStaticType<Exactly<int?>>();
207+
y.expectStaticType<Exactly<int>>();
209208
}
210209

211210
void testExplicitPropertyReferenceOnLhsOfNotEquals(C c) {
@@ -221,9 +220,9 @@ void testExplicitPropertyReferenceOnLhsOfNotEquals(C c) {
221220
y = null;
222221
}
223222
// In analyzing the `!=` check, flow analysis assumes that `c._f` has its
224-
// unpromoted type (`Object?`), so both branches of the `if` are
225-
// reachable. Therefore both `x` and `y` should both be demoted here.
226-
x.expectStaticType<Exactly<int?>>();
223+
// promoted type (`Null`), so only the `y = null` branch is
224+
// reachable. Therefore only `y` should be demoted here.
225+
x.expectStaticType<Exactly<int>>();
227226
y.expectStaticType<Exactly<int?>>();
228227
}
229228

@@ -240,9 +239,9 @@ void testExplicitPropertyReferenceOnRhsOfNotEquals(C c) {
240239
y = null;
241240
}
242241
// In analyzing the `!=` check, flow analysis assumes that `c._f` has its
243-
// unpromoted type (`Object?`), so both branches of the `if` are
244-
// reachable. Therefore both `x` and `y` should both be demoted here.
245-
x.expectStaticType<Exactly<int?>>();
242+
// promoted type (`Null`), so only the `y = null` branch is
243+
// reachable. Therefore only `y` should be demoted here.
244+
x.expectStaticType<Exactly<int>>();
246245
y.expectStaticType<Exactly<int?>>();
247246
}
248247

0 commit comments

Comments
 (0)