Add SwitchStatementTypeAnalysisResult.requiresExhaustivenessValidation.

This boolean tells the analyzer and front end whether it is required
to run the exhaustiness algorithm on the switch statement (and to
report an error if the switch isn't exhaustive).

Bug: https://github.com/dart-lang/sdk/issues/50585
Change-Id: I8c95e563bd59a83cf5e1b94170af5c4f8b5c6496
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/274925
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2022-12-13 14:35:37 +00:00 committed by Commit Queue
parent a29cd031d3
commit 9169abc8f4
4 changed files with 87 additions and 1 deletions

View file

@ -125,6 +125,14 @@ class SwitchStatementTypeAnalysisResult<Type> {
/// Whether the last case body in the switch statement terminated.
final bool lastCaseTerminates;
/// If `true`, patterns support is enabled, there is no default clause, and
/// the static type of the scrutinee expression is an "always exhaustive"
/// type. Therefore, flow analysis has assumed (without checking) that the
/// switch statement is exhaustive. So at a later stage of compilation, the
/// exhaustiveness checking algorithm should check whether this switch
/// statement was exhaustive, and report a compile-time error if it wasn't.
final bool requiresExhaustivenessValidation;
/// The static type of the scrutinee expression.
final Type scrutineeType;
@ -132,6 +140,7 @@ class SwitchStatementTypeAnalysisResult<Type> {
required this.hasDefault,
required this.isExhaustive,
required this.lastCaseTerminates,
required this.requiresExhaustivenessValidation,
required this.scrutineeType,
});
}

View file

@ -1352,18 +1352,23 @@ mixin TypeAnalyzer<
}
// Stack: (Expression, numExecutionPaths * StatementCase)
bool isExhaustive;
bool requiresExhaustivenessValidation;
if (hasDefault) {
isExhaustive = true;
requiresExhaustivenessValidation = false;
} else if (options.patternsEnabled) {
isExhaustive = isAlwaysExhaustiveType(scrutineeType);
requiresExhaustivenessValidation =
isExhaustive = isAlwaysExhaustiveType(scrutineeType);
} else {
isExhaustive = isLegacySwitchExhaustive(node, scrutineeType);
requiresExhaustivenessValidation = false;
}
flow.switchStatement_end(isExhaustive);
return new SwitchStatementTypeAnalysisResult<Type>(
hasDefault: hasDefault,
isExhaustive: isExhaustive,
lastCaseTerminates: lastCaseTerminates,
requiresExhaustivenessValidation: requiresExhaustivenessValidation,
scrutineeType: scrutineeType,
);
}

View file

@ -327,12 +327,15 @@ Statement switch_(Expression expression, List<_SwitchStatementMember> cases,
bool? expectHasDefault,
bool? expectIsExhaustive,
bool? expectLastCaseTerminates,
bool? expectRequiresExhaustivenessValidation,
String? expectScrutineeType}) =>
new _SwitchStatement(expression, cases, isLegacyExhaustive,
location: computeLocation(),
expectHasDefault: expectHasDefault,
expectIsExhaustive: expectIsExhaustive,
expectLastCaseTerminates: expectLastCaseTerminates,
expectRequiresExhaustivenessValidation:
expectRequiresExhaustivenessValidation,
expectScrutineeType: expectScrutineeType);
Expression switchExpr(Expression expression, List<ExpressionCase> cases) =>
@ -4288,6 +4291,8 @@ class _SwitchStatement extends Statement {
final bool? expectLastCaseTerminates;
final bool? expectRequiresExhaustivenessValidation;
final String? expectScrutineeType;
_SwitchStatement(this.scrutinee, this.cases, this.isLegacyExhaustive,
@ -4295,6 +4300,7 @@ class _SwitchStatement extends Statement {
required this.expectHasDefault,
required this.expectIsExhaustive,
required this.expectLastCaseTerminates,
required this.expectRequiresExhaustivenessValidation,
required this.expectScrutineeType});
@override
@ -4344,6 +4350,8 @@ class _SwitchStatement extends Statement {
expect(analysisResult.isExhaustive, expectIsExhaustive ?? anything);
expect(analysisResult.lastCaseTerminates,
expectLastCaseTerminates ?? anything);
expect(analysisResult.requiresExhaustivenessValidation,
expectRequiresExhaustivenessValidation ?? anything);
expect(analysisResult.scrutineeType.type, expectScrutineeType ?? anything);
h.irBuilder.apply(
'switch',

View file

@ -1314,6 +1314,70 @@ main() {
], expectedErrors: {});
});
});
group('requiresExhaustivenessValidation:', () {
test('When a `default` clause is present', () {
h.addExhaustiveness('E', true);
h.run([
switch_(
expr('E'),
[
default_.then([
break_(),
]),
],
expectRequiresExhaustivenessValidation: false,
),
]);
});
test('When the scrutinee is an always-exhaustive type', () {
h.addExhaustiveness('E', true);
h.run([
switch_(
expr('E'),
[
expr('E').pattern.then([
break_(),
]),
],
expectRequiresExhaustivenessValidation: true,
),
]);
});
test('When the scrutinee is not an always-exhaustive type', () {
h.addExhaustiveness('C', false);
h.run([
switch_(
expr('C'),
[
expr('C').pattern.then([
break_(),
]),
],
expectRequiresExhaustivenessValidation: false,
),
]);
});
test('When pattern support is disabled', () {
h.patternsEnabled = false;
h.addExhaustiveness('E', true);
h.run([
switch_(
expr('E'),
[
expr('E').pattern.then([
break_(),
]),
],
isLegacyExhaustive: true,
expectRequiresExhaustivenessValidation: false,
),
]);
});
});
});
group('Variable declaration:', () {