Skip to content

Commit fadf6ec

Browse files
committed
[Sema] Remove @unknown default requirement for package enums.
`@unknown default` in switch statements are required for resilient enums since they might be modified with new fields in the future and modules defining the enums are generally not built together with the consuming modules. However, if the modules are in the same package, they are required to be built together, thus the requirement for `@unknown default` can be skipped. This PR removes the need for that, enabling less boilerplate. Note this change only impacts typecheck and not SIL gen. Resolves rdar://130015149.
1 parent 29a4b8a commit fadf6ec

File tree

5 files changed

+418
-177
lines changed

5 files changed

+418
-177
lines changed

include/swift/AST/Decl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4726,6 +4726,15 @@ class EnumDecl final : public NominalTypeDecl {
47264726
/// \sa isEffectivelyExhaustive
47274727
bool isFormallyExhaustive(const DeclContext *useDC) const;
47284728

4729+
/// True if \s isFormallyExhaustive is true or the use site's module belongs
4730+
/// to the same package as this enum's defining module. If in same package
4731+
/// even though `isFormallyExhaustive` is false, we can skip requiring
4732+
/// `@unknown default` at the use site switch stmts because package modules
4733+
/// are expected to be built together whether they are resiliently built or
4734+
/// not. Used for diagnostics during typechecks only; if
4735+
/// `isFormallyExhaustive` is false, it should be reflected in SILgen.
4736+
bool treatAsExhaustiveForDiags(const DeclContext *useDC) const;
4737+
47294738
/// True if the enum can be exhaustively switched within a function defined
47304739
/// within \p M, with \p expansion specifying whether the function is
47314740
/// inlinable.

lib/AST/Decl.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6559,6 +6559,11 @@ bool EnumDecl::hasOnlyCasesWithoutAssociatedValues() const {
65596559
return !hasAssociatedValues;
65606560
}
65616561

6562+
bool EnumDecl::treatAsExhaustiveForDiags(const DeclContext *useDC) const {
6563+
return isFormallyExhaustive(useDC) ||
6564+
(useDC && getModuleContext()->inSamePackage(useDC->getParentModule()));
6565+
}
6566+
65626567
bool EnumDecl::isFormallyExhaustive(const DeclContext *useDC) const {
65636568
// Enums explicitly marked frozen are exhaustive.
65646569
if (getAttrs().hasAttribute<FrozenAttr>())
@@ -6576,14 +6581,10 @@ bool EnumDecl::isFormallyExhaustive(const DeclContext *useDC) const {
65766581
return true;
65776582

65786583
// Non-public, non-versioned enums are always exhaustive.
6579-
AccessScope accessScope = getFormalAccessScope(/*useDC*/nullptr,
6580-
/*respectVersioned*/true);
6581-
// Both public and package enums should behave the same unless
6582-
// package enum is optimized with bypassing resilience checks.
6584+
AccessScope accessScope = getFormalAccessScope(/*useDC*/ nullptr,
6585+
/*respectVersioned*/ true);
65836586
if (!accessScope.isPublicOrPackage())
65846587
return true;
6585-
if (useDC && bypassResilienceInPackage(useDC->getParentModule()))
6586-
return true;
65876588

65886589
// All other checks are use-site specific; with no further information, the
65896590
// enum must be treated non-exhaustively.
@@ -6615,11 +6616,13 @@ bool EnumDecl::isEffectivelyExhaustive(ModuleDecl *M,
66156616
if (isObjC())
66166617
return false;
66176618

6618-
// Otherwise, the only non-exhaustive cases are those that don't have a fixed
6619-
// layout.
6620-
assert(isFormallyExhaustive(M) == !isResilient(M,ResilienceExpansion::Maximal)
6621-
&& "ignoring the effects of @inlinable, @testable, and @objc, "
6622-
"these should match up");
6619+
// Otherwise, the only non-exhaustive enums are those that don't have
6620+
// a fixed layout; however, they are treated as exhaustive if package
6621+
// optimization is enabled.
6622+
assert((isFormallyExhaustive(M) || bypassResilienceInPackage(M)) ==
6623+
!isResilient(M, ResilienceExpansion::Maximal) &&
6624+
"ignoring the effects of @inlinable, @testable, and @objc, "
6625+
"these should match up");
66236626
return !isResilient(M, expansion);
66246627
}
66256628

lib/Sema/TypeCheckSwitchStmt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ namespace {
861861
constElemSpaces);
862862
});
863863

864-
if (!E->isFormallyExhaustive(DC)) {
864+
if (!E->treatAsExhaustiveForDiags(DC)) {
865865
arr.push_back(Space::forUnknown(/*allowedButNotRequired*/false));
866866
} else if (!E->getAttrs().hasAttribute<FrozenAttr>()) {
867867
arr.push_back(Space::forUnknown(/*allowedButNotRequired*/true));

0 commit comments

Comments
 (0)