Skip to content

[6.0][Sema] Non-exhaustive switch statements are always an error in Swift 6. #73481

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -7031,10 +7031,9 @@ WARNING(redundant_particular_literal_case,none,
NOTE(redundant_particular_literal_case_here,none,
"first occurrence of identical literal pattern is here", ())

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

ERROR(override_nsobject_hashvalue_error,none,
"'NSObject.hashValue' is not overridable; "
Expand Down
1 change: 1 addition & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ UPCOMING_FEATURE(InferSendableFromCaptures, 418, 6)
UPCOMING_FEATURE(ImplicitOpenExistentials, 352, 6)
UPCOMING_FEATURE(RegionBasedIsolation, 414, 6)
UPCOMING_FEATURE(DynamicActorIsolation, 423, 6)
UPCOMING_FEATURE(NonfrozenEnumExhaustivity, 192, 6)

// Swift 7
UPCOMING_FEATURE(ExistentialAny, 335, 7)
Expand Down
4 changes: 0 additions & 4 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,10 +454,6 @@ namespace swift {
/// Diagnose uses of NSCoding with classes that have unstable mangled names.
bool EnableNSKeyedArchiverDiagnostics = true;

/// Diagnose switches over non-frozen enums that do not have catch-all
/// cases.
bool EnableNonFrozenEnumExhaustivityDiagnostics = false;

/// Regex for the passes that should report passed and missed optimizations.
///
/// These are shared_ptrs so that this class remains copyable.
Expand Down
2 changes: 2 additions & 0 deletions lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,8 @@ static bool usesFeatureTransferringArgsAndResults(Decl *decl) {

UNINTERESTING_FEATURE(DynamicActorIsolation)

UNINTERESTING_FEATURE(NonfrozenEnumExhaustivity)

UNINTERESTING_FEATURE(BorrowingSwitch)

UNINTERESTING_FEATURE(ClosureIsolation)
Expand Down
9 changes: 5 additions & 4 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1084,10 +1084,11 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
OPT_disable_nskeyedarchiver_diagnostics,
Opts.EnableNSKeyedArchiverDiagnostics);

Opts.EnableNonFrozenEnumExhaustivityDiagnostics =
Args.hasFlag(OPT_enable_nonfrozen_enum_exhaustivity_diagnostics,
OPT_disable_nonfrozen_enum_exhaustivity_diagnostics,
Opts.isSwiftVersionAtLeast(5));
if (Args.hasFlag(OPT_enable_nonfrozen_enum_exhaustivity_diagnostics,
OPT_disable_nonfrozen_enum_exhaustivity_diagnostics,
Opts.isSwiftVersionAtLeast(5))) {
Opts.enableFeature(Feature::NonfrozenEnumExhaustivity);
}

if (Arg *A = Args.getLastArg(OPT_Rpass_EQ))
Opts.OptimizationRemarkPassedPattern =
Expand Down
20 changes: 12 additions & 8 deletions lib/Sema/TypeCheckSwitchStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1128,13 +1128,13 @@ namespace {
// Decide whether we want an error or a warning.
std::optional<decltype(diag::non_exhaustive_switch)> mainDiagType =
diag::non_exhaustive_switch;
bool downgrade = false;
if (unknownCase) {
switch (defaultReason) {
case RequiresDefault::EmptySwitchBody:
llvm_unreachable("there's an @unknown case; the body can't be empty");
case RequiresDefault::No:
if (!uncovered.isEmpty())
mainDiagType = diag::non_exhaustive_switch_warn;
downgrade = !uncovered.isEmpty();
break;
case RequiresDefault::UncoveredSwitch:
case RequiresDefault::SpaceTooLarge: {
Expand All @@ -1157,7 +1157,7 @@ namespace {
case DowngradeToWarning::ForUnknownCase: {
if (Context.LangOpts.DebuggerSupport ||
Context.LangOpts.Playground ||
!Context.LangOpts.EnableNonFrozenEnumExhaustivityDiagnostics) {
!Context.LangOpts.hasFeature(Feature::NonfrozenEnumExhaustivity)) {
// Don't require covering unknown cases in the debugger or in
// playgrounds.
return;
Expand All @@ -1170,7 +1170,8 @@ namespace {
theEnum->getParentModule()->isSystemModule();
}
DE.diagnose(startLoc, diag::non_exhaustive_switch_unknown_only,
subjectType, shouldIncludeFutureVersionComment);
subjectType, shouldIncludeFutureVersionComment)
.warnUntilSwiftVersion(6);
mainDiagType = std::nullopt;
}
break;
Expand All @@ -1187,7 +1188,8 @@ namespace {
return;
case RequiresDefault::UncoveredSwitch: {
OS << tok::kw_default << ":\n" << placeholder << "\n";
DE.diagnose(startLoc, mainDiagType.value());
DE.diagnose(startLoc, mainDiagType.value())
.warnUntilSwiftVersionIf(downgrade, 6);
DE.diagnose(startLoc, diag::missing_several_cases, /*default*/true)
.fixItInsert(insertLoc, buffer.str());
}
Expand All @@ -1205,8 +1207,10 @@ namespace {
if (uncovered.isEmpty()) return;

// Check if we still have to emit the main diagnostic.
if (mainDiagType.has_value())
DE.diagnose(startLoc, mainDiagType.value());
if (mainDiagType.has_value()) {
DE.diagnose(startLoc, mainDiagType.value())
.warnUntilSwiftVersionIf(downgrade, 6);
}

// Add notes to explain what's missing.
auto processUncoveredSpaces =
Expand Down Expand Up @@ -1255,7 +1259,7 @@ namespace {
// will later decompose the space into cases.
continue;
}
if (!Context.LangOpts.EnableNonFrozenEnumExhaustivityDiagnostics)
if (!Context.LangOpts.hasFeature(Feature::NonfrozenEnumExhaustivity))
continue;

// This can occur if the switch is empty and the subject type is an
Expand Down
14 changes: 11 additions & 3 deletions test/ClangImporter/enum-exhaustivity-system.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// RUN: %target-swift-frontend -typecheck %s -Xcc -isystem -Xcc %S/Inputs/custom-modules -verify -enable-nonfrozen-enum-exhaustivity-diagnostics
// RUN: %target-swift-frontend -typecheck %s -Xcc -isystem -Xcc %S/Inputs/custom-modules -verify -swift-version 5 -verify-additional-prefix swift5-
// RUN: %target-swift-frontend -typecheck %s -Xcc -isystem -Xcc %S/Inputs/custom-modules -verify -swift-version 6 -verify-additional-prefix swift6-

import EnumExhaustivity

func test(_ value: RegularEnum, _ exhaustiveValue: ExhaustiveEnum) {
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"}}
switch value {
// expected-swift5-warning@-1 {{switch covers known cases, but 'RegularEnum' may have additional unknown values, possibly added in future versions}}
// expected-swift6-error@-2 {{switch covers known cases, but 'RegularEnum' may have additional unknown values, possibly added in future versions}}
// expected-note@-3 {{handle unknown values using "@unknown default"}}
case .A: break
case .B: break
}
Expand All @@ -29,7 +33,11 @@ func testAttributes(
case .A, .B: break
}

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"}}
switch retetb {
// expected-swift5-warning@-1 {{switch covers known cases, but 'RegularEnumTurnedExhaustiveThenBackViaAPINotes' may have additional unknown values, possibly added in future versions}}
// expected-swift6-error@-2 {{switch covers known cases, but 'RegularEnumTurnedExhaustiveThenBackViaAPINotes' may have additional unknown values, possibly added in future versions}}
// expected-note@-3 {{handle unknown values using "@unknown default"}}

case .A, .B: break
}

Expand Down
34 changes: 34 additions & 0 deletions test/Compatibility/exhaustive_switch_swift_6.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %target-typecheck-verify-swift -swift-version 6 -enable-library-evolution

enum OverlyLargeSpaceEnum {
case case0
case case1
case case2
case case3
case case4
case case5
case case6
case case7
case case8
case case9
case case10
case case11
}

func testSwitch() -> Bool {
switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { // expected-error {{switch must be exhaustive}}
// expected-note@-1 {{add missing case: '(.case11, _)'}}
case (.case0, _): return true
case (.case1, _): return true
case (.case2, _): return true
case (.case3, _): return true
case (.case4, _): return true
case (.case5, _): return true
case (.case6, _): return true
case (.case7, _): return true
case (.case8, _): return true
case (.case9, _): return true
case (.case10, _): return true
@unknown default: return false
}
}
1 change: 1 addition & 0 deletions test/Sema/exhaustive_switch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution %S/Inputs/exhaustive_switch_testable_helper.swift -emit-module -o %t
// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-library-evolution -I %t
// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-library-evolution -enable-nonfrozen-enum-exhaustivity-diagnostics -I %t
// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-library-evolution -enable-upcoming-feature NonfrozenEnumExhaustivity -I %t

import exhaustive_switch_testable_helper

Expand Down