Skip to content

Commit 4f01b04

Browse files
stereotype441Commit Queue
authored andcommitted
Add tests reproducing language issue 4127.
These tests exercise the current (unintended) behavior described dart-lang/language#4127, which was previously not well tested. Adding these tests acts as a safeguard to make sure that we don't change the current behavior by accident. If/when we decide to fix dart-lang/language#4127, the test expectations will need to be updated. Bug: dart-lang/language#4127 Change-Id: I02fd1d393038a304401d11cf2c19e97755ba90a0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/389584 Reviewed-by: Kallen Tu <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent 2b44c29 commit 4f01b04

File tree

2 files changed

+313
-0
lines changed

2 files changed

+313
-0
lines changed

pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7249,6 +7249,54 @@ main() {
72497249
]);
72507250
});
72517251
});
7252+
7253+
group('and equality:', () {
7254+
test('promoted type ignored on LHS', () {
7255+
// Normally flow analysis understands when an `if` test is guaranteed to
7256+
// succeed (or fail) based on the static types of the LHS and RHS. But
7257+
// due to https://github.com/dart-lang/language/issues/4127, this
7258+
// doesn't fully work when the LHS or RHS is a property reference; in
7259+
// that case, the unpromoted type is used.
7260+
7261+
// This test is here to make sure we don't change the existing behavior
7262+
// by accident; if/when we fix #4127, this test should be changed
7263+
// accordingly.
7264+
h.addMember('C', 'f', 'Object?', promotable: true);
7265+
h.thisType = 'C';
7266+
h.run([
7267+
if_(thisProperty('f').isNot('Null'), [return_()]),
7268+
checkPromoted(thisProperty('f'), 'Null'),
7269+
if_(thisProperty('f').eq(nullLiteral), [
7270+
checkReachable(true),
7271+
], [
7272+
checkReachable(true),
7273+
]),
7274+
]);
7275+
});
7276+
7277+
test('promoted type ignored on RHS', () {
7278+
// Normally flow analysis understands when an `if` test is guaranteed to
7279+
// succeed (or fail) based on the static types of the LHS and RHS. But
7280+
// due to https://github.com/dart-lang/language/issues/4127, this
7281+
// doesn't fully work when the LHS or RHS is a property reference; in
7282+
// that case, the unpromoted type is used.
7283+
7284+
// This test is here to make sure we don't change the existing behavior
7285+
// by accident; if/when we fix #4127, this test should be changed
7286+
// accordingly.
7287+
h.addMember('C', 'f', 'Object?', promotable: true);
7288+
h.thisType = 'C';
7289+
h.run([
7290+
if_(thisProperty('f').isNot('Null'), [return_()]),
7291+
checkPromoted(thisProperty('f'), 'Null'),
7292+
if_(nullLiteral.eq(thisProperty('f')), [
7293+
checkReachable(true),
7294+
], [
7295+
checkReachable(true),
7296+
]),
7297+
]);
7298+
});
7299+
});
72527300
});
72537301

72547302
group('Patterns:', () {
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
// Copyright (c) 2024, 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 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.
10+
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.
13+
14+
import '../static_type_helper.dart';
15+
16+
class C {
17+
final Object? _f;
18+
C(this._f);
19+
20+
void testImplicitThisReferenceOnLhsOfEquals() {
21+
int? x = 0;
22+
int? y = 0;
23+
x.expectStaticType<Exactly<int>>();
24+
y.expectStaticType<Exactly<int>>();
25+
if (_f is! Null) return;
26+
_f.expectStaticType<Exactly<Null>>();
27+
if (_f == null) {
28+
x = null;
29+
} else {
30+
y = null;
31+
}
32+
// 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.
35+
x.expectStaticType<Exactly<int?>>();
36+
y.expectStaticType<Exactly<int?>>();
37+
}
38+
39+
void testImplicitThisReferenceOnRhsOfEquals() {
40+
int? x = 0;
41+
int? y = 0;
42+
x.expectStaticType<Exactly<int>>();
43+
y.expectStaticType<Exactly<int>>();
44+
if (_f is! Null) return;
45+
_f.expectStaticType<Exactly<Null>>();
46+
if (null == _f) {
47+
x = null;
48+
} else {
49+
y = null;
50+
}
51+
// 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.
54+
x.expectStaticType<Exactly<int?>>();
55+
y.expectStaticType<Exactly<int?>>();
56+
}
57+
58+
void testImplicitThisReferenceOnLhsOfNotEquals() {
59+
int? x = 0;
60+
int? y = 0;
61+
x.expectStaticType<Exactly<int>>();
62+
y.expectStaticType<Exactly<int>>();
63+
if (_f is! Null) return;
64+
_f.expectStaticType<Exactly<Null>>();
65+
if (_f != null) {
66+
x = null;
67+
} else {
68+
y = null;
69+
}
70+
// 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?>>();
74+
y.expectStaticType<Exactly<int?>>();
75+
}
76+
77+
void testImplicitThisReferenceOnRhsOfNotEquals() {
78+
int? x = 0;
79+
int? y = 0;
80+
x.expectStaticType<Exactly<int>>();
81+
y.expectStaticType<Exactly<int>>();
82+
if (_f is! Null) return;
83+
_f.expectStaticType<Exactly<Null>>();
84+
if (null != _f) {
85+
x = null;
86+
} else {
87+
y = null;
88+
}
89+
// 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?>>();
93+
y.expectStaticType<Exactly<int?>>();
94+
}
95+
96+
void testExplicitThisReferenceOnLhsOfEquals() {
97+
int? x = 0;
98+
int? y = 0;
99+
x.expectStaticType<Exactly<int>>();
100+
y.expectStaticType<Exactly<int>>();
101+
if (this._f is! Null) return;
102+
this._f.expectStaticType<Exactly<Null>>();
103+
if (this._f == null) {
104+
x = null;
105+
} else {
106+
y = null;
107+
}
108+
// 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.
111+
x.expectStaticType<Exactly<int?>>();
112+
y.expectStaticType<Exactly<int?>>();
113+
}
114+
115+
void testExplicitThisReferenceOnRhsOfEquals() {
116+
int? x = 0;
117+
int? y = 0;
118+
x.expectStaticType<Exactly<int>>();
119+
y.expectStaticType<Exactly<int>>();
120+
if (this._f is! Null) return;
121+
this._f.expectStaticType<Exactly<Null>>();
122+
if (null == this._f) {
123+
x = null;
124+
} else {
125+
y = null;
126+
}
127+
// 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.
130+
x.expectStaticType<Exactly<int?>>();
131+
y.expectStaticType<Exactly<int?>>();
132+
}
133+
134+
void testExplicitThisReferenceOnLhsOfNotEquals() {
135+
int? x = 0;
136+
int? y = 0;
137+
x.expectStaticType<Exactly<int>>();
138+
y.expectStaticType<Exactly<int>>();
139+
if (this._f is! Null) return;
140+
this._f.expectStaticType<Exactly<Null>>();
141+
if (this._f != null) {
142+
x = null;
143+
} else {
144+
y = null;
145+
}
146+
// 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?>>();
150+
y.expectStaticType<Exactly<int?>>();
151+
}
152+
153+
void testExplicitThisReferenceOnRhsOfNotEquals() {
154+
int? x = 0;
155+
int? y = 0;
156+
x.expectStaticType<Exactly<int>>();
157+
y.expectStaticType<Exactly<int>>();
158+
if (this._f is! Null) return;
159+
this._f.expectStaticType<Exactly<Null>>();
160+
if (null != this._f) {
161+
x = null;
162+
} else {
163+
y = null;
164+
}
165+
// 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?>>();
169+
y.expectStaticType<Exactly<int?>>();
170+
}
171+
}
172+
173+
void testExplicitPropertyReferenceOnLhsOfEquals(C c) {
174+
int? x = 0;
175+
int? y = 0;
176+
x.expectStaticType<Exactly<int>>();
177+
y.expectStaticType<Exactly<int>>();
178+
if (c._f is! Null) return;
179+
c._f.expectStaticType<Exactly<Null>>();
180+
if (c._f == null) {
181+
x = null;
182+
} else {
183+
y = null;
184+
}
185+
// 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.
188+
x.expectStaticType<Exactly<int?>>();
189+
y.expectStaticType<Exactly<int?>>();
190+
}
191+
192+
void testExplicitPropertyReferenceOnRhsOfEquals(C c) {
193+
int? x = 0;
194+
int? y = 0;
195+
x.expectStaticType<Exactly<int>>();
196+
y.expectStaticType<Exactly<int>>();
197+
if (c._f is! Null) return;
198+
c._f.expectStaticType<Exactly<Null>>();
199+
if (null == c._f) {
200+
x = null;
201+
} else {
202+
y = null;
203+
}
204+
// 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.
207+
x.expectStaticType<Exactly<int?>>();
208+
y.expectStaticType<Exactly<int?>>();
209+
}
210+
211+
void testExplicitPropertyReferenceOnLhsOfNotEquals(C c) {
212+
int? x = 0;
213+
int? y = 0;
214+
x.expectStaticType<Exactly<int>>();
215+
y.expectStaticType<Exactly<int>>();
216+
if (c._f is! Null) return;
217+
c._f.expectStaticType<Exactly<Null>>();
218+
if (c._f != null) {
219+
x = null;
220+
} else {
221+
y = null;
222+
}
223+
// 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?>>();
227+
y.expectStaticType<Exactly<int?>>();
228+
}
229+
230+
void testExplicitPropertyReferenceOnRhsOfNotEquals(C c) {
231+
int? x = 0;
232+
int? y = 0;
233+
x.expectStaticType<Exactly<int>>();
234+
y.expectStaticType<Exactly<int>>();
235+
if (c._f is! Null) return;
236+
c._f.expectStaticType<Exactly<Null>>();
237+
if (null != c._f) {
238+
x = null;
239+
} else {
240+
y = null;
241+
}
242+
// 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?>>();
246+
y.expectStaticType<Exactly<int?>>();
247+
}
248+
249+
main() {
250+
for (var value in [null, '']) {
251+
var c = C(value);
252+
c.testImplicitThisReferenceOnLhsOfEquals();
253+
c.testImplicitThisReferenceOnRhsOfEquals();
254+
c.testImplicitThisReferenceOnLhsOfNotEquals();
255+
c.testImplicitThisReferenceOnRhsOfNotEquals();
256+
c.testExplicitThisReferenceOnLhsOfEquals();
257+
c.testExplicitThisReferenceOnRhsOfEquals();
258+
c.testExplicitThisReferenceOnLhsOfNotEquals();
259+
c.testExplicitThisReferenceOnRhsOfNotEquals();
260+
testExplicitPropertyReferenceOnLhsOfEquals(c);
261+
testExplicitPropertyReferenceOnRhsOfEquals(c);
262+
testExplicitPropertyReferenceOnLhsOfNotEquals(c);
263+
testExplicitPropertyReferenceOnRhsOfNotEquals(c);
264+
}
265+
}

0 commit comments

Comments
 (0)