Skip to content

[Sema] Remove @unknown default requirement for package enums. #75872

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 1 commit into from
Aug 15, 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
9 changes: 9 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4726,6 +4726,15 @@ class EnumDecl final : public NominalTypeDecl {
/// \sa isEffectivelyExhaustive
bool isFormallyExhaustive(const DeclContext *useDC) const;

/// True if \s isFormallyExhaustive is true or the use site's module belongs
/// to the same package as this enum's defining module. If in same package
/// even though `isFormallyExhaustive` is false, we can skip requiring
/// `@unknown default` at the use site switch stmts because package modules
/// are expected to be built together whether they are resiliently built or
/// not. Used for diagnostics during typechecks only; if
/// `isFormallyExhaustive` is false, it should be reflected in SILgen.
bool treatAsExhaustiveForDiags(const DeclContext *useDC) const;

/// True if the enum can be exhaustively switched within a function defined
/// within \p M, with \p expansion specifying whether the function is
/// inlinable.
Expand Down
25 changes: 14 additions & 11 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6559,6 +6559,11 @@ bool EnumDecl::hasOnlyCasesWithoutAssociatedValues() const {
return !hasAssociatedValues;
}

bool EnumDecl::treatAsExhaustiveForDiags(const DeclContext *useDC) const {
return isFormallyExhaustive(useDC) ||
(useDC && getModuleContext()->inSamePackage(useDC->getParentModule()));
}

bool EnumDecl::isFormallyExhaustive(const DeclContext *useDC) const {
// Enums explicitly marked frozen are exhaustive.
if (getAttrs().hasAttribute<FrozenAttr>())
Expand All @@ -6576,14 +6581,10 @@ bool EnumDecl::isFormallyExhaustive(const DeclContext *useDC) const {
return true;

// Non-public, non-versioned enums are always exhaustive.
AccessScope accessScope = getFormalAccessScope(/*useDC*/nullptr,
/*respectVersioned*/true);
// Both public and package enums should behave the same unless
// package enum is optimized with bypassing resilience checks.
AccessScope accessScope = getFormalAccessScope(/*useDC*/ nullptr,
/*respectVersioned*/ true);
if (!accessScope.isPublicOrPackage())
return true;
if (useDC && bypassResilienceInPackage(useDC->getParentModule()))
return true;

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

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

Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckSwitchStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ namespace {
constElemSpaces);
});

if (!E->isFormallyExhaustive(DC)) {
if (!E->treatAsExhaustiveForDiags(DC)) {
arr.push_back(Space::forUnknown(/*allowedButNotRequired*/false));
} else if (!E->getAttrs().hasAttribute<FrozenAttr>()) {
arr.push_back(Space::forUnknown(/*allowedButNotRequired*/true));
Expand Down
Loading