Skip to content

Commit 0657fff

Browse files
committed
[beta] Patterns flow analysis: recognize [...] (and related patterns) as trivially exhaustive.
If a list pattern consists of a single rest pattern, and that rest pattern is guaranteed to match, then the whole list pattern is guaranteed to match as well (provided that the matched value type is a subtype of the list pattern's required type). Bug: dart-lang/language#2980 Change-Id: Ibdd3b2315676e763a0c44b6c0adf7ac528d349e3 Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/294622 Fixes: #52018 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/294682 Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent 6ea4f75 commit 0657fff

File tree

3 files changed

+70
-19
lines changed

3 files changed

+70
-19
lines changed

pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,8 @@ mixin TypeAnalyzer<
800800
flow.promoteForPattern(
801801
matchedType: matchedType,
802802
knownType: requiredType,
803-
matchMayFailEvenIfCorrectType: true);
803+
matchMayFailEvenIfCorrectType:
804+
!(elements.length == 1 && isRestPatternElement(elements[0])));
804805
// Stack: ()
805806
Node? previousRestPattern;
806807
Map<int, Error>? duplicateRestPatternErrors;

pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7481,15 +7481,65 @@ main() {
74817481
});
74827482

74837483
group('List pattern:', () {
7484-
test('Not guaranteed to match', () {
7485-
h.run([
7486-
switch_(expr('Object'), [
7487-
listPattern([]).then([break_()]),
7488-
default_.then([
7489-
checkReachable(true),
7484+
group('Not guaranteed to match:', () {
7485+
test('Empty list', () {
7486+
h.run([
7487+
switch_(expr('List<Object>'), [
7488+
listPattern([]).then([break_()]),
7489+
default_.then([
7490+
checkReachable(true),
7491+
]),
74907492
]),
7491-
]),
7492-
]);
7493+
]);
7494+
});
7495+
7496+
test('Single non-rest element', () {
7497+
h.run([
7498+
switch_(expr('List<Object>'), [
7499+
listPattern([wildcard()]).then([break_()]),
7500+
default_.then([
7501+
checkReachable(true),
7502+
]),
7503+
]),
7504+
]);
7505+
});
7506+
7507+
test('Rest pattern with subpattern that may fail to match', () {
7508+
h.run([
7509+
switch_(expr('List<Object>'), [
7510+
listPattern([listPatternRestElement(listPattern([]))])
7511+
.then([break_()]),
7512+
default_.then([
7513+
checkReachable(true),
7514+
])
7515+
])
7516+
]);
7517+
});
7518+
});
7519+
7520+
group('Guaranteed to match:', () {
7521+
test('Rest pattern with no subpattern', () {
7522+
h.run([
7523+
switch_(expr('List<Object>'), [
7524+
listPattern([listPatternRestElement()]).then([break_()]),
7525+
default_.then([
7526+
checkReachable(false),
7527+
])
7528+
])
7529+
]);
7530+
});
7531+
7532+
test('Rest pattern with subpattern that always matches', () {
7533+
h.run([
7534+
switch_(expr('List<Object>'), [
7535+
listPattern([listPatternRestElement(wildcard())])
7536+
.then([break_()]),
7537+
default_.then([
7538+
checkReachable(false),
7539+
])
7540+
])
7541+
]);
7542+
});
74937543
});
74947544

74957545
test('Promotes', () {

tests/language/patterns/switch_trivial_exhaustiveness_error_test.dart

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -128,44 +128,44 @@ void testListContainingRestPatternAndNonRestPattern(List<Object> x) {
128128
}
129129

130130
void testListContainingOnlyRestPattern(List<Object> x) {
131-
// TODO(paulberry): this should be trivially exhaustive
131+
// Trivially exhaustive
132132
bool? y;
133133
switch (x) {
134134
case [...]:
135135
y = true;
136136
}
137-
y.expectStaticType<Exactly<bool?>>();
137+
y.expectStaticType<Exactly<bool>>();
138138
}
139139

140140
void testListContainingOnlyRestPatternWithSubpatternWildcard(List<Object> x) {
141-
// TODO(paulberry): this should be trivially exhaustive
141+
// Trivially exhaustive
142142
bool? y;
143143
switch (x) {
144144
case [..._]:
145145
y = true;
146146
}
147-
y.expectStaticType<Exactly<bool?>>();
147+
y.expectStaticType<Exactly<bool>>();
148148
}
149149

150150
void testListContainingOnlyRestPatternWithSubpatternAnyList(List<Object> x) {
151-
// TODO(paulberry): this should be trivially exhaustive
151+
// Trivially exhaustive
152152
bool? y;
153153
switch (x) {
154154
case [...[...]]:
155155
y = true;
156156
}
157-
y.expectStaticType<Exactly<bool?>>();
157+
y.expectStaticType<Exactly<bool>>();
158158
}
159159

160160
void testListContainingOnlyRestPatternWithSubpatternObjectPattern(
161161
List<Object> x) {
162-
// TODO(paulberry): this should be trivially exhaustive
162+
// Trivially exhaustive
163163
bool? y;
164164
switch (x) {
165165
case [...List()]:
166166
y = true;
167167
}
168-
y.expectStaticType<Exactly<bool?>>();
168+
y.expectStaticType<Exactly<bool>>();
169169
}
170170

171171
void testListContainingOnlyRestPatternWithSubpatternOther(List<Object> x) {
@@ -179,13 +179,13 @@ void testListContainingOnlyRestPatternWithSubpatternOther(List<Object> x) {
179179
}
180180

181181
void testListSupertype(List<Object> x) {
182-
// TODO(paulberry): this should be trivially exhaustive
182+
// Trivially exhaustive
183183
bool? y;
184184
switch (x) {
185185
case <Object?>[...]:
186186
y = true;
187187
}
188-
y.expectStaticType<Exactly<bool?>>();
188+
y.expectStaticType<Exactly<bool>>();
189189
}
190190

191191
void testListSubtype(List<Object> x) {

0 commit comments

Comments
 (0)