Skip to content

Commit 8bf8c24

Browse files
authored
Merge pull request #80503 from xedin/extensible-attr
[AST/Sema] Add `@extensible` attribute on `enum` declarations
2 parents 95bd086 + bf19481 commit 8bf8c24

25 files changed

+103
-78
lines changed

include/swift/AST/Decl.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
735735
HasAnyUnavailableDuringLoweringValues : 1
736736
);
737737

738-
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+8,
738+
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+8,
739739
/// If the module is compiled as static library.
740740
StaticLibrary : 1,
741741

@@ -804,10 +804,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
804804
SerializePackageEnabled : 1,
805805

806806
/// Whether this module has enabled strict memory safety checking.
807-
StrictMemorySafety : 1,
808-
809-
/// Whether this module has enabled `ExtensibleEnums` feature.
810-
ExtensibleEnums : 1
807+
StrictMemorySafety : 1
811808
);
812809

813810
SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2,

include/swift/AST/DeclAttr.def

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,12 @@ SIMPLE_DECL_ATTR(constInitialized, ConstInitialized,
879879
168)
880880
DECL_ATTR_FEATURE_REQUIREMENT(ConstInitialized, CompileTimeValues)
881881

882-
LAST_DECL_ATTR(ConstInitialized)
882+
SIMPLE_DECL_ATTR(extensible, Extensible,
883+
OnEnum,
884+
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | ForbiddenInABIAttr,
885+
169)
886+
887+
LAST_DECL_ATTR(Extensible)
883888

884889
#undef DECL_ATTR_ALIAS
885890
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8577,6 +8577,19 @@ GROUPED_WARNING(
85778577
"behavior",
85788578
(StringRef, DeclAttribute))
85798579

8580+
//===----------------------------------------------------------------------===//
8581+
// MARK: @extensible Attribute
8582+
//===----------------------------------------------------------------------===//
8583+
8584+
ERROR(extensible_attr_on_frozen_type,none,
8585+
"cannot use '@extensible' together with '@frozen'", ())
8586+
8587+
ERROR(extensible_attr_on_internal_type,none,
8588+
"'@extensible' attribute can only be applied to public or package "
8589+
"declarations, but %0 is "
8590+
"%select{private|fileprivate|internal|%error|%error|%error}1",
8591+
(DeclName, AccessLevel))
8592+
85808593
//===----------------------------------------------------------------------===//
85818594
// MARK: SwiftSettings
85828595
//===----------------------------------------------------------------------===//

include/swift/AST/Module.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -840,14 +840,6 @@ class ModuleDecl
840840
Bits.ModuleDecl.ObjCNameLookupCachePopulated = value;
841841
}
842842

843-
bool supportsExtensibleEnums() const {
844-
return Bits.ModuleDecl.ExtensibleEnums;
845-
}
846-
847-
void setSupportsExtensibleEnums(bool value = true) {
848-
Bits.ModuleDecl.ExtensibleEnums = value;
849-
}
850-
851843
/// For the main module, retrieves the list of primary source files being
852844
/// compiled, that is, the files we're generating code for.
853845
ArrayRef<SourceFile *> getPrimarySourceFiles() const;

include/swift/Basic/Features.def

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -495,11 +495,6 @@ ADOPTABLE_EXPERIMENTAL_FEATURE(AsyncCallerExecution, false)
495495
/// Allow custom availability domains to be defined and referenced.
496496
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(CustomAvailability, true)
497497

498-
/// Allow public enumerations to be extensible by default
499-
/// regardless of whether the module they are declared in
500-
/// is resilient or not.
501-
EXPERIMENTAL_FEATURE(ExtensibleEnums, true)
502-
503498
/// Allow isolated conformances.
504499
EXPERIMENTAL_FEATURE(IsolatedConformances, true)
505500

include/swift/Serialization/Validation.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ class ExtendedValidationInfo {
150150
unsigned AllowNonResilientAccess: 1;
151151
unsigned SerializePackageEnabled: 1;
152152
unsigned StrictMemorySafety: 1;
153-
unsigned SupportsExtensibleEnums : 1;
154153
} Bits;
155154

156155
public:
@@ -272,11 +271,6 @@ class ExtendedValidationInfo {
272271
version, SourceLoc(), /*Diags=*/nullptr))
273272
SwiftInterfaceCompilerVersion = genericVersion.value();
274273
}
275-
276-
bool supportsExtensibleEnums() const { return Bits.SupportsExtensibleEnums; }
277-
void setSupportsExtensibleEnums(bool val) {
278-
Bits.SupportsExtensibleEnums = val;
279-
}
280274
};
281275

282276
struct SearchPath {

lib/AST/ASTDumper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4925,6 +4925,7 @@ class PrintAttribute : public AttributeVisitor<PrintAttribute, void, Label>,
49254925
TRIVIAL_ATTR_PRINTER(Used, used)
49264926
TRIVIAL_ATTR_PRINTER(WarnUnqualifiedAccess, warn_unqualified_access)
49274927
TRIVIAL_ATTR_PRINTER(WeakLinked, weak_linked)
4928+
TRIVIAL_ATTR_PRINTER(Extensible, extensible)
49284929

49294930
#undef TRIVIAL_ATTR_PRINTER
49304931

lib/AST/ASTPrinter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
402402
DeclAttrKind::RestatedObjCConformance,
403403
DeclAttrKind::NonSendable,
404404
DeclAttrKind::AllowFeatureSuppression,
405+
DeclAttrKind::Extensible,
405406
};
406407

407408
return result;

lib/AST/Decl.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6854,14 +6854,11 @@ bool EnumDecl::treatAsExhaustiveForDiags(const DeclContext *useDC) const {
68546854
if (enumModule->inSamePackage(useDC->getParentModule()))
68556855
return true;
68566856

6857-
// If the module where enum is declared supports extensible enumerations
6858-
// and this enum is not explicitly marked as "@frozen", cross-module
6859-
// access cannot be exhaustive and requires `@unknown default:`.
6860-
if (enumModule->supportsExtensibleEnums() &&
6861-
!getAttrs().hasAttribute<FrozenAttr>()) {
6862-
if (useDC != enumModule->getDeclContext())
6863-
return false;
6864-
}
6857+
// When the enum is marked as `@extensible` cross-module access
6858+
// cannot be exhaustive and requires `@unknown default:`.
6859+
if (getAttrs().hasAttribute<ExtensibleAttr>() &&
6860+
enumModule != useDC->getParentModule())
6861+
return false;
68656862
}
68666863

68676864
return isFormallyExhaustive(useDC);

lib/AST/FeatureSet.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ UNINTERESTING_FEATURE(SuppressedAssociatedTypes)
124124
UNINTERESTING_FEATURE(StructLetDestructuring)
125125
UNINTERESTING_FEATURE(MacrosOnImports)
126126
UNINTERESTING_FEATURE(AsyncCallerExecution)
127-
UNINTERESTING_FEATURE(ExtensibleEnums)
128127
UNINTERESTING_FEATURE(KeyPathWithMethodMembers)
129128

130129
static bool usesFeatureNonescapableTypes(Decl *decl) {

lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ extension ASTGenVisitor {
228228
.dynamicCallable,
229229
.eagerMove,
230230
.exported,
231+
.extensible,
231232
.discardableResult,
232233
.disfavoredOverload,
233234
.dynamicMemberLookup,

lib/Frontend/Frontend.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,8 +1472,6 @@ ModuleDecl *CompilerInstance::getMainModule() const {
14721472
MainModule->setSerializePackageEnabled();
14731473
if (Invocation.getLangOptions().hasFeature(Feature::StrictMemorySafety))
14741474
MainModule->setStrictMemorySafety(true);
1475-
if (Invocation.getLangOptions().hasFeature(Feature::ExtensibleEnums))
1476-
MainModule->setSupportsExtensibleEnums(true);
14771475

14781476
configureAvailabilityDomains(getASTContext(),
14791477
Invocation.getFrontendOptions(), MainModule);

lib/Sema/TypeCheckAttr.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,21 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
241241
}
242242
}
243243

244+
void visitExtensibleAttr(ExtensibleAttr *attr) {
245+
auto *E = cast<EnumDecl>(D);
246+
247+
if (D->getAttrs().hasAttribute<FrozenAttr>()) {
248+
diagnoseAndRemoveAttr(attr, diag::extensible_attr_on_frozen_type);
249+
return;
250+
}
251+
252+
if (E->getFormalAccess() < AccessLevel::Package) {
253+
diagnoseAndRemoveAttr(attr, diag::extensible_attr_on_internal_type,
254+
E->getName(), E->getFormalAccess());
255+
return;
256+
}
257+
}
258+
244259
void visitAlignmentAttr(AlignmentAttr *attr) {
245260
// Alignment must be a power of two.
246261
auto value = attr->getValue();

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,6 +1615,7 @@ namespace {
16151615
UNINTERESTING_ATTR(Isolated)
16161616
UNINTERESTING_ATTR(Optimize)
16171617
UNINTERESTING_ATTR(Exclusivity)
1618+
UNINTERESTING_ATTR(Extensible)
16181619
UNINTERESTING_ATTR(NoLocks)
16191620
UNINTERESTING_ATTR(NoAllocation)
16201621
UNINTERESTING_ATTR(NoRuntime)

lib/Sema/TypeCheckSwitchStmt.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,10 +1159,7 @@ namespace {
11591159
auto *enumModule = theEnum->getParentModule();
11601160
shouldIncludeFutureVersionComment =
11611161
enumModule->isSystemModule() ||
1162-
enumModule->supportsExtensibleEnums();
1163-
// Since the module enabled `ExtensibleEnums` feature they
1164-
// opted-in all of their clients into exhaustivity errors.
1165-
shouldDowngradeToWarning = !enumModule->supportsExtensibleEnums();
1162+
theEnum->getAttrs().hasAttribute<ExtensibleAttr>();
11661163
}
11671164
DE.diagnose(startLoc, diag::non_exhaustive_switch_unknown_only,
11681165
subjectType, shouldIncludeFutureVersionComment)

lib/Serialization/ModuleFile.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -714,11 +714,6 @@ class ModuleFile
714714
/// \c true if this module was built with strict memory safety.
715715
bool strictMemorySafety() const { return Core->strictMemorySafety(); }
716716

717-
/// \c true if this module was built with `ExtensibleEnums` feature enabled.
718-
bool supportsExtensibleEnums() const {
719-
return Core->supportsExtensibleEnums();
720-
}
721-
722717
/// Associates this module file with the AST node representing it.
723718
///
724719
/// Checks that the file is compatible with the AST module it's being loaded

lib/Serialization/ModuleFileSharedCore.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,6 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor,
224224
case options_block::STRICT_MEMORY_SAFETY:
225225
extendedInfo.setStrictMemorySafety(true);
226226
break;
227-
case options_block::EXTENSIBLE_ENUMS:
228-
extendedInfo.setSupportsExtensibleEnums(true);
229-
break;
230227
default:
231228
// Unknown options record, possibly for use by a future version of the
232229
// module format.
@@ -1508,7 +1505,6 @@ ModuleFileSharedCore::ModuleFileSharedCore(
15081505
Bits.AllowNonResilientAccess = extInfo.allowNonResilientAccess();
15091506
Bits.SerializePackageEnabled = extInfo.serializePackageEnabled();
15101507
Bits.StrictMemorySafety = extInfo.strictMemorySafety();
1511-
Bits.SupportsExtensibleEnums = extInfo.supportsExtensibleEnums();
15121508
MiscVersion = info.miscVersion;
15131509
SDKVersion = info.sdkVersion;
15141510
ModuleABIName = extInfo.getModuleABIName();

lib/Serialization/ModuleFileSharedCore.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -421,11 +421,8 @@ class ModuleFileSharedCore {
421421
/// Whether this module enabled strict memory safety.
422422
unsigned StrictMemorySafety : 1;
423423

424-
/// Whether this module enabled has `ExtensibleEnums` feature enabled.
425-
unsigned SupportsExtensibleEnums : 1;
426-
427424
// Explicitly pad out to the next word boundary.
428-
unsigned : 1;
425+
unsigned : 2;
429426
} Bits = {};
430427
static_assert(sizeof(ModuleBits) <= 8, "The bit set should be small");
431428

@@ -684,8 +681,6 @@ class ModuleFileSharedCore {
684681

685682
bool strictMemorySafety() const { return Bits.StrictMemorySafety; }
686683

687-
bool supportsExtensibleEnums() const { return Bits.SupportsExtensibleEnums; }
688-
689684
/// How should \p dependency be loaded for a transitive import via \c this?
690685
///
691686
/// If \p importNonPublicDependencies, more transitive dependencies

lib/Serialization/ModuleFormat.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 933; // isConstantValue
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 935; // remove ExtensibleEnums feature
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -974,8 +974,7 @@ namespace options_block {
974974
CXX_STDLIB_KIND,
975975
PUBLIC_MODULE_NAME,
976976
SWIFT_INTERFACE_COMPILER_VERSION,
977-
STRICT_MEMORY_SAFETY,
978-
EXTENSIBLE_ENUMS,
977+
STRICT_MEMORY_SAFETY
979978
};
980979

981980
using SDKPathLayout = BCRecordLayout<
@@ -1085,10 +1084,6 @@ namespace options_block {
10851084
SWIFT_INTERFACE_COMPILER_VERSION,
10861085
BCBlob // version tuple
10871086
>;
1088-
1089-
using ExtensibleEnumsLayout = BCRecordLayout<
1090-
EXTENSIBLE_ENUMS
1091-
>;
10921087
}
10931088

10941089
/// The record types within the input block.

lib/Serialization/Serialization.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,11 +1186,6 @@ void Serializer::writeHeader() {
11861186
static_cast<uint8_t>(M->getCXXStdlibKind()));
11871187
}
11881188

1189-
if (M->supportsExtensibleEnums()) {
1190-
options_block::ExtensibleEnumsLayout ExtensibleEnums(Out);
1191-
ExtensibleEnums.emit(ScratchRecord);
1192-
}
1193-
11941189
if (Options.SerializeOptionsForDebugging) {
11951190
options_block::SDKPathLayout SDKPath(Out);
11961191
options_block::XCCLayout XCC(Out);

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,8 +1100,6 @@ LoadedFile *SerializedModuleLoaderBase::loadAST(
11001100
if (!loadedModuleFile->getModulePackageName().empty()) {
11011101
M.setPackageName(Ctx.getIdentifier(loadedModuleFile->getModulePackageName()));
11021102
}
1103-
if (loadedModuleFile->supportsExtensibleEnums())
1104-
M.setSupportsExtensibleEnums();
11051103
M.setUserModuleVersion(loadedModuleFile->getUserModuleVersion());
11061104
M.setSwiftInterfaceCompilerVersion(
11071105
loadedModuleFile->getSwiftInterfaceCompilerVersion());

test/IDE/complete_decl_attribute.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ struct _S {
325325
// ON_MEMBER_LAST-DAG: Keyword/None: freestanding[#Declaration Attribute#]; name=freestanding
326326
// ON_MEMBER_LAST-DAG: Keyword/None: storageRestrictions[#Declaration Attribute#]; name=storageRestrictions
327327
// ON_MEMBER_LAST-DAG: Keyword/None: lifetime[#Declaration Attribute#]; name=lifetime
328+
// ON_MEMBER_LAST-DAG: Keyword/None: extensible[#Declaration Attribute#]; name=extensible
328329
// ON_MEMBER_LAST-NOT: Keyword
329330
// ON_MEMBER_LAST-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
330331
// ON_MEMBER_LAST-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyPropertyWrapper[#Property Wrapper#]; name=MyPropertyWrapper
@@ -397,6 +398,7 @@ func dummy2() {}
397398
// KEYWORD_LAST-DAG: Keyword/None: attached[#Declaration Attribute#]; name=attached
398399
// KEYWORD_LAST-DAG: Keyword/None: storageRestrictions[#Declaration Attribute#]; name=storageRestrictions
399400
// KEYWORD_LAST-DAG: Keyword/None: lifetime[#Declaration Attribute#]; name=lifetime
401+
// KEYWORD_LAST-DAG: Keyword/None: extensible[#Declaration Attribute#]; name=extensible
400402
// KEYWORD_LAST-NOT: Keyword
401403
// KEYWORD_LAST-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct
402404
// KEYWORD_LAST-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyGenericPropertyWrapper[#Property Wrapper#]; name=MyGenericPropertyWrapper

test/ModuleInterface/attrs.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,11 @@ public func testExecutionConcurrent() async {}
9494
@execution(caller)
9595
public func testExecutionCaller() async {}
9696
// CHECK: @execution(caller) public func testExecutionCaller() async
97+
98+
// CHECK-NOT: @extensible
99+
// CHECK: public enum TestExtensible
100+
@extensible
101+
public enum TestExtensible {
102+
case a
103+
case b
104+
}

test/ModuleInterface/extensible_enums.swift

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
/// Build the library
66
// RUN: %target-swift-frontend -emit-module %t/src/Lib.swift \
77
// RUN: -module-name Lib \
8-
// RUN: -emit-module-path %t/Lib.swiftmodule \
9-
// RUN: -enable-experimental-feature ExtensibleEnums
8+
// RUN: -emit-module-path %t/Lib.swiftmodule
109

1110
// Check that the errors are produced when using enums from module with `ExtensibleEnums` feature enabled.
1211
// RUN: %target-swift-frontend -typecheck %t/src/TestChecking.swift \
@@ -19,19 +18,17 @@
1918
// RUN: %target-swift-frontend -emit-module %t/src/Lib.swift \
2019
// RUN: -module-name Lib \
2120
// RUN: -package-name Test \
22-
// RUN: -emit-module-path %t/Lib.swiftmodule \
23-
// RUN: -enable-experimental-feature ExtensibleEnums
21+
// RUN: -emit-module-path %t/Lib.swiftmodule
2422

2523
// Different module but the same package
2624
// RUN: %target-swift-frontend -typecheck %t/src/TestSamePackage.swift \
2725
// RUN: -swift-version 5 -module-name Client -I %t \
2826
// RUN: -package-name Test \
2927
// RUN: -verify
3028

31-
// REQUIRES: swift_feature_ExtensibleEnums
32-
3329
//--- Lib.swift
3430

31+
@extensible
3532
public enum E {
3633
case a
3734
}
@@ -57,10 +54,10 @@ func test_same_module(e: E, f: F) {
5754
import Lib
5855

5956
func test(e: E, f: F) {
60-
// `E` is not marked as `@frozen` which means it gets new semantics
57+
// `E` is marked as `@extensible` which means it gets new semantics
6158

6259
switch e {
63-
// expected-error@-1 {{switch covers known cases, but 'E' may have additional unknown values, possibly added in future versions}}
60+
// expected-warning@-1 {{switch covers known cases, but 'E' may have additional unknown values, possibly added in future versions; this is an error in the Swift 6 language mode}}
6461
// expected-note@-2 {{handle unknown values using "@unknown default"}}
6562
case .a: break
6663
}
@@ -70,7 +67,7 @@ func test(e: E, f: F) {
7067
@unknown default: break
7168
}
7269

73-
// `F` is marked as `@frozen` which means regular rules apply even with `ExtensibleEnums` feature enabled.
70+
// `F` is marked as `@frozen` which means regular rules apply.
7471

7572
switch f { // Ok (no errors because `F` is `@frozen`)
7673
case .a: break

0 commit comments

Comments
 (0)