Skip to content

Commit e7db0ec

Browse files
authored
Merge pull request #73472 from hborla/exhaustive-switch-error
[Sema] Non-exhaustive switch statements are always an error in Swift 6.
2 parents 3aa133f + a7920ce commit e7db0ec

File tree

9 files changed

+69
-23
lines changed

9 files changed

+69
-23
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7082,10 +7082,9 @@ WARNING(redundant_particular_literal_case,none,
70827082
NOTE(redundant_particular_literal_case_here,none,
70837083
"first occurrence of identical literal pattern is here", ())
70847084

7085-
WARNING(non_exhaustive_switch_warn,none, "switch must be exhaustive", ())
7086-
WARNING(non_exhaustive_switch_unknown_only,none,
7087-
"switch covers known cases, but %0 may have additional unknown values"
7088-
"%select{|, possibly added in future versions}1", (Type, bool))
7085+
ERROR(non_exhaustive_switch_unknown_only,none,
7086+
"switch covers known cases, but %0 may have additional unknown values"
7087+
"%select{|, possibly added in future versions}1", (Type, bool))
70897088

70907089
ERROR(override_nsobject_hashvalue_error,none,
70917090
"'NSObject.hashValue' is not overridable; "

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ UPCOMING_FEATURE(InferSendableFromCaptures, 418, 6)
194194
UPCOMING_FEATURE(ImplicitOpenExistentials, 352, 6)
195195
UPCOMING_FEATURE(RegionBasedIsolation, 414, 6)
196196
UPCOMING_FEATURE(DynamicActorIsolation, 423, 6)
197+
UPCOMING_FEATURE(NonfrozenEnumExhaustivity, 192, 6)
197198

198199
// Swift 7
199200
UPCOMING_FEATURE(ExistentialAny, 335, 7)

include/swift/Basic/LangOptions.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -454,10 +454,6 @@ namespace swift {
454454
/// Diagnose uses of NSCoding with classes that have unstable mangled names.
455455
bool EnableNSKeyedArchiverDiagnostics = true;
456456

457-
/// Diagnose switches over non-frozen enums that do not have catch-all
458-
/// cases.
459-
bool EnableNonFrozenEnumExhaustivityDiagnostics = false;
460-
461457
/// Regex for the passes that should report passed and missed optimizations.
462458
///
463459
/// These are shared_ptrs so that this class remains copyable.

lib/AST/FeatureSet.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,8 @@ static bool usesFeatureTransferringArgsAndResults(Decl *decl) {
653653

654654
UNINTERESTING_FEATURE(DynamicActorIsolation)
655655

656+
UNINTERESTING_FEATURE(NonfrozenEnumExhaustivity)
657+
656658
UNINTERESTING_FEATURE(BorrowingSwitch)
657659

658660
UNINTERESTING_FEATURE(ClosureIsolation)

lib/Frontend/CompilerInvocation.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,10 +1084,11 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
10841084
OPT_disable_nskeyedarchiver_diagnostics,
10851085
Opts.EnableNSKeyedArchiverDiagnostics);
10861086

1087-
Opts.EnableNonFrozenEnumExhaustivityDiagnostics =
1088-
Args.hasFlag(OPT_enable_nonfrozen_enum_exhaustivity_diagnostics,
1089-
OPT_disable_nonfrozen_enum_exhaustivity_diagnostics,
1090-
Opts.isSwiftVersionAtLeast(5));
1087+
if (Args.hasFlag(OPT_enable_nonfrozen_enum_exhaustivity_diagnostics,
1088+
OPT_disable_nonfrozen_enum_exhaustivity_diagnostics,
1089+
Opts.isSwiftVersionAtLeast(5))) {
1090+
Opts.enableFeature(Feature::NonfrozenEnumExhaustivity);
1091+
}
10911092

10921093
if (Arg *A = Args.getLastArg(OPT_Rpass_EQ))
10931094
Opts.OptimizationRemarkPassedPattern =

lib/Sema/TypeCheckSwitchStmt.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,13 +1128,13 @@ namespace {
11281128
// Decide whether we want an error or a warning.
11291129
std::optional<decltype(diag::non_exhaustive_switch)> mainDiagType =
11301130
diag::non_exhaustive_switch;
1131+
bool downgrade = false;
11311132
if (unknownCase) {
11321133
switch (defaultReason) {
11331134
case RequiresDefault::EmptySwitchBody:
11341135
llvm_unreachable("there's an @unknown case; the body can't be empty");
11351136
case RequiresDefault::No:
1136-
if (!uncovered.isEmpty())
1137-
mainDiagType = diag::non_exhaustive_switch_warn;
1137+
downgrade = !uncovered.isEmpty();
11381138
break;
11391139
case RequiresDefault::UncoveredSwitch:
11401140
case RequiresDefault::SpaceTooLarge: {
@@ -1157,7 +1157,7 @@ namespace {
11571157
case DowngradeToWarning::ForUnknownCase: {
11581158
if (Context.LangOpts.DebuggerSupport ||
11591159
Context.LangOpts.Playground ||
1160-
!Context.LangOpts.EnableNonFrozenEnumExhaustivityDiagnostics) {
1160+
!Context.LangOpts.hasFeature(Feature::NonfrozenEnumExhaustivity)) {
11611161
// Don't require covering unknown cases in the debugger or in
11621162
// playgrounds.
11631163
return;
@@ -1170,7 +1170,8 @@ namespace {
11701170
theEnum->getParentModule()->isSystemModule();
11711171
}
11721172
DE.diagnose(startLoc, diag::non_exhaustive_switch_unknown_only,
1173-
subjectType, shouldIncludeFutureVersionComment);
1173+
subjectType, shouldIncludeFutureVersionComment)
1174+
.warnUntilSwiftVersion(6);
11741175
mainDiagType = std::nullopt;
11751176
}
11761177
break;
@@ -1187,7 +1188,8 @@ namespace {
11871188
return;
11881189
case RequiresDefault::UncoveredSwitch: {
11891190
OS << tok::kw_default << ":\n" << placeholder << "\n";
1190-
DE.diagnose(startLoc, mainDiagType.value());
1191+
DE.diagnose(startLoc, mainDiagType.value())
1192+
.warnUntilSwiftVersionIf(downgrade, 6);
11911193
DE.diagnose(startLoc, diag::missing_several_cases, /*default*/true)
11921194
.fixItInsert(insertLoc, buffer.str());
11931195
}
@@ -1205,8 +1207,10 @@ namespace {
12051207
if (uncovered.isEmpty()) return;
12061208

12071209
// Check if we still have to emit the main diagnostic.
1208-
if (mainDiagType.has_value())
1209-
DE.diagnose(startLoc, mainDiagType.value());
1210+
if (mainDiagType.has_value()) {
1211+
DE.diagnose(startLoc, mainDiagType.value())
1212+
.warnUntilSwiftVersionIf(downgrade, 6);
1213+
}
12101214

12111215
// Add notes to explain what's missing.
12121216
auto processUncoveredSpaces =
@@ -1255,7 +1259,7 @@ namespace {
12551259
// will later decompose the space into cases.
12561260
continue;
12571261
}
1258-
if (!Context.LangOpts.EnableNonFrozenEnumExhaustivityDiagnostics)
1262+
if (!Context.LangOpts.hasFeature(Feature::NonfrozenEnumExhaustivity))
12591263
continue;
12601264

12611265
// This can occur if the switch is empty and the subject type is an

test/ClangImporter/enum-exhaustivity-system.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
// RUN: %target-swift-frontend -typecheck %s -Xcc -isystem -Xcc %S/Inputs/custom-modules -verify -enable-nonfrozen-enum-exhaustivity-diagnostics
1+
// RUN: %target-swift-frontend -typecheck %s -Xcc -isystem -Xcc %S/Inputs/custom-modules -verify -swift-version 5 -verify-additional-prefix swift5-
2+
// RUN: %target-swift-frontend -typecheck %s -Xcc -isystem -Xcc %S/Inputs/custom-modules -verify -swift-version 6 -verify-additional-prefix swift6-
23

34
import EnumExhaustivity
45

56
func test(_ value: RegularEnum, _ exhaustiveValue: ExhaustiveEnum) {
6-
switch value { // expected-warning {{switch covers known cases, but 'RegularEnum' may have additional unknown values, possibly added in future versions}} expected-note {{handle unknown values using "@unknown default"}}
7+
switch value {
8+
// expected-swift5-warning@-1 {{switch covers known cases, but 'RegularEnum' may have additional unknown values, possibly added in future versions}}
9+
// expected-swift6-error@-2 {{switch covers known cases, but 'RegularEnum' may have additional unknown values, possibly added in future versions}}
10+
// expected-note@-3 {{handle unknown values using "@unknown default"}}
711
case .A: break
812
case .B: break
913
}
@@ -29,7 +33,11 @@ func testAttributes(
2933
case .A, .B: break
3034
}
3135

32-
switch retetb { // expected-warning {{switch covers known cases, but 'RegularEnumTurnedExhaustiveThenBackViaAPINotes' may have additional unknown values, possibly added in future versions}} expected-note {{handle unknown values using "@unknown default"}}
36+
switch retetb {
37+
// expected-swift5-warning@-1 {{switch covers known cases, but 'RegularEnumTurnedExhaustiveThenBackViaAPINotes' may have additional unknown values, possibly added in future versions}}
38+
// expected-swift6-error@-2 {{switch covers known cases, but 'RegularEnumTurnedExhaustiveThenBackViaAPINotes' may have additional unknown values, possibly added in future versions}}
39+
// expected-note@-3 {{handle unknown values using "@unknown default"}}
40+
3341
case .A, .B: break
3442
}
3543

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 6 -enable-library-evolution
2+
3+
enum OverlyLargeSpaceEnum {
4+
case case0
5+
case case1
6+
case case2
7+
case case3
8+
case case4
9+
case case5
10+
case case6
11+
case case7
12+
case case8
13+
case case9
14+
case case10
15+
case case11
16+
}
17+
18+
func testSwitch() -> Bool {
19+
switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { // expected-error {{switch must be exhaustive}}
20+
// expected-note@-1 {{add missing case: '(.case11, _)'}}
21+
case (.case0, _): return true
22+
case (.case1, _): return true
23+
case (.case2, _): return true
24+
case (.case3, _): return true
25+
case (.case4, _): return true
26+
case (.case5, _): return true
27+
case (.case6, _): return true
28+
case (.case7, _): return true
29+
case (.case8, _): return true
30+
case (.case9, _): return true
31+
case (.case10, _): return true
32+
@unknown default: return false
33+
}
34+
}

test/Sema/exhaustive_switch.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution %S/Inputs/exhaustive_switch_testable_helper.swift -emit-module -o %t
33
// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-library-evolution -I %t
44
// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-library-evolution -enable-nonfrozen-enum-exhaustivity-diagnostics -I %t
5+
// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-library-evolution -enable-upcoming-feature NonfrozenEnumExhaustivity -I %t
56

67
import exhaustive_switch_testable_helper
78

0 commit comments

Comments
 (0)