diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index a0e2bf98f2f9a..800df3c733ec1 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -244,6 +244,10 @@ class DeclAttribute : public AttributeBase { SWIFT_INLINE_BITFIELD(LifetimeAttr, DeclAttribute, 1, isUnderscored : 1 ); + + SWIFT_INLINE_BITFIELD(NonexhaustiveAttr, DeclAttribute, NumNonexhaustiveModeBits, + mode : NumNonexhaustiveModeBits + ); } Bits; // clang-format on @@ -3334,6 +3338,35 @@ class ABIAttr : public DeclAttribute { } }; +/// Defines a @nonexhaustive attribute. +class NonexhaustiveAttr : public DeclAttribute { +public: + NonexhaustiveAttr(SourceLoc atLoc, SourceRange range, NonexhaustiveMode mode, + bool implicit = false) + : DeclAttribute(DeclAttrKind::Nonexhaustive, atLoc, range, implicit) { + Bits.NonexhaustiveAttr.mode = unsigned(mode); + } + + NonexhaustiveAttr(NonexhaustiveMode mode) + : NonexhaustiveAttr(SourceLoc(), SourceRange(), mode) {} + + NonexhaustiveMode getMode() const { + return NonexhaustiveMode(Bits.NonexhaustiveAttr.mode); + } + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DeclAttrKind::Nonexhaustive; + } + + NonexhaustiveAttr *clone(ASTContext &ctx) const { + return new (ctx) NonexhaustiveAttr(AtLoc, Range, getMode(), isImplicit()); + } + + bool isEquivalent(const NonexhaustiveAttr *other, Decl *attachedTo) const { + return getMode() == other->getMode(); + } +}; + /// Attributes that may be applied to declarations. class DeclAttributes { /// Linked list of declaration attributes. diff --git a/include/swift/AST/AttrKind.h b/include/swift/AST/AttrKind.h index 1d1a6226f7a91..3091abab36ecb 100644 --- a/include/swift/AST/AttrKind.h +++ b/include/swift/AST/AttrKind.h @@ -158,6 +158,17 @@ enum : unsigned { InheritActorContextModifier::Last_InheritActorContextKind)) }; +enum class NonexhaustiveMode : uint8_t { + Error = 0, + Warning, + Last_NonexhaustiveMode = Warning +}; + +enum : unsigned { + NumNonexhaustiveModeBits = countBitsUsed(static_cast( + NonexhaustiveMode::Last_NonexhaustiveMode)) +}; + enum class DeclAttrKind : unsigned { #define DECL_ATTR(_, CLASS, ...) CLASS, #define LAST_DECL_ATTR(CLASS) Last_DeclAttr = CLASS, diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 18efc38168122..ad5b44afb8c8a 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -751,7 +751,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi HasAnyUnavailableDuringLoweringValues : 1 ); - 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, + 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, /// If the module is compiled as static library. StaticLibrary : 1, @@ -820,10 +820,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi SerializePackageEnabled : 1, /// Whether this module has enabled strict memory safety checking. - StrictMemorySafety : 1, - - /// Whether this module has enabled `ExtensibleEnums` feature. - ExtensibleEnums : 1 + StrictMemorySafety : 1 ); SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2, diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index ba2e8c28a28c6..8b7c4ad24b252 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -876,11 +876,19 @@ SIMPLE_DECL_ATTR(constInitialized, ConstInitialized, 168) DECL_ATTR_FEATURE_REQUIREMENT(ConstInitialized, CompileTimeValues) +DECL_ATTR(nonexhaustive, Nonexhaustive, + OnEnum, + ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | ForbiddenInABIAttr, + 169) +DECL_ATTR_FEATURE_REQUIREMENT(Nonexhaustive, NonexhaustiveAttribute) + SIMPLE_DECL_ATTR(concurrent, Concurrent, OnFunc | OnConstructor | OnSubscript | OnVar, ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 170) +// Unused '171': Used to be `@preEnumExtensibility` + LAST_DECL_ATTR(Concurrent) #undef DECL_ATTR_ALIAS diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 0c646d2c93458..12d2f8c18d999 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8711,6 +8711,19 @@ GROUPED_WARNING( "behavior", (StringRef)) +//===----------------------------------------------------------------------===// +// MARK: @nonexhaustive and @preEnumExtensibility Attributes +//===----------------------------------------------------------------------===// + +ERROR(nonexhaustive_attr_on_frozen_type,none, + "cannot use '@nonexhaustive' together with '@frozen'", ()) + +ERROR(nonexhaustive_attr_on_internal_type,none, + "'@nonexhaustive' attribute can only be applied to public or package " + "declarations, but %0 is " + "%select{private|fileprivate|internal|%error|%error|%error}1", + (DeclName, AccessLevel)) + //===----------------------------------------------------------------------===// // MARK: `using` declaration //===----------------------------------------------------------------------===// diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 8be2729b9376a..a9728b0ea31fb 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -169,6 +169,7 @@ IDENTIFIER(value) IDENTIFIER_WITH_NAME(value_, "_value") IDENTIFIER(Void) IDENTIFIER(WinSDK) +IDENTIFIER(warn) IDENTIFIER(with) IDENTIFIER(withArguments) IDENTIFIER(withKeywordArguments) diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index f079dc4a2ea9c..4ab0e26182552 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -835,14 +835,6 @@ class ModuleDecl Bits.ModuleDecl.ObjCNameLookupCachePopulated = value; } - bool supportsExtensibleEnums() const { - return Bits.ModuleDecl.ExtensibleEnums; - } - - void setSupportsExtensibleEnums(bool value = true) { - Bits.ModuleDecl.ExtensibleEnums = value; - } - /// For the main module, retrieves the list of primary source files being /// compiled, that is, the files we're generating code for. ArrayRef getPrimarySourceFiles() const; diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index c6b3de80207eb..d2294c9e3fe9a 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -500,11 +500,6 @@ SUPPRESSIBLE_EXPERIMENTAL_FEATURE(AddressableTypes, true) /// Allow custom availability domains to be defined and referenced. EXPERIMENTAL_FEATURE(CustomAvailability, true) -/// Allow public enumerations to be extensible by default -/// regardless of whether the module they are declared in -/// is resilient or not. -EXPERIMENTAL_FEATURE(ExtensibleEnums, true) - /// Syntax sugar features for concurrency. EXPERIMENTAL_FEATURE(ConcurrencySyntaxSugar, true) @@ -523,6 +518,9 @@ EXPERIMENTAL_FEATURE(AllowRuntimeSymbolDeclarations, true) /// Optimize copies of ObjectiveC blocks. EXPERIMENTAL_FEATURE(CopyBlockOptimization, true) +/// Allow use of `@nonexhaustive` on public enums +SUPPRESSIBLE_EXPERIMENTAL_FEATURE(NonexhaustiveAttribute, false) + /// Allow use of `using` declaration that control default isolation /// in a file scope. EXPERIMENTAL_FEATURE(DefaultIsolationPerFile, false) diff --git a/include/swift/Parse/IDEInspectionCallbacks.h b/include/swift/Parse/IDEInspectionCallbacks.h index 45c2634cbdd8e..7147a5f945c5d 100644 --- a/include/swift/Parse/IDEInspectionCallbacks.h +++ b/include/swift/Parse/IDEInspectionCallbacks.h @@ -40,7 +40,8 @@ enum class ParameterizedDeclAttributeKind { FreestandingMacro, AttachedMacro, StorageRestrictions, - InheritActorContext + InheritActorContext, + Nonexhaustive, }; /// A bit of a hack. When completing inside the '@storageRestrictions' diff --git a/include/swift/Serialization/Validation.h b/include/swift/Serialization/Validation.h index 545e1900dd4c6..7d5eeb4c635df 100644 --- a/include/swift/Serialization/Validation.h +++ b/include/swift/Serialization/Validation.h @@ -150,7 +150,6 @@ class ExtendedValidationInfo { unsigned AllowNonResilientAccess: 1; unsigned SerializePackageEnabled: 1; unsigned StrictMemorySafety: 1; - unsigned SupportsExtensibleEnums : 1; } Bits; public: @@ -272,11 +271,6 @@ class ExtendedValidationInfo { version, SourceLoc(), /*Diags=*/nullptr)) SwiftInterfaceCompilerVersion = genericVersion.value(); } - - bool supportsExtensibleEnums() const { return Bits.SupportsExtensibleEnums; } - void setSupportsExtensibleEnums(bool val) { - Bits.SupportsExtensibleEnums = val; - } }; struct SearchPath { diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 9a9ac6fb8e16d..794858c5d7f35 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -5064,6 +5064,7 @@ class PrintAttribute : public AttributeVisitor, TRIVIAL_ATTR_PRINTER(Used, used) TRIVIAL_ATTR_PRINTER(WarnUnqualifiedAccess, warn_unqualified_access) TRIVIAL_ATTR_PRINTER(WeakLinked, weak_linked) + TRIVIAL_ATTR_PRINTER(Nonexhaustive, nonexhaustive) TRIVIAL_ATTR_PRINTER(Concurrent, concurrent) #undef TRIVIAL_ATTR_PRINTER diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index e6cf506e37d51..bb6328b808b7d 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3325,6 +3325,13 @@ suppressingFeatureAddressableTypes(PrintOptions &options, action(); } +static void +suppressingFeatureNonexhaustiveAttribute(PrintOptions &options, + llvm::function_ref action) { + ExcludeAttrRAII scope(options.ExcludeAttrList, DeclAttrKind::Nonexhaustive); + action(); +} + /// Suppress the printing of a particular feature. static void suppressingFeature(PrintOptions &options, Feature feature, llvm::function_ref action) { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index b275161608f95..83cfdb6b46258 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1725,6 +1725,19 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, break; } + case DeclAttrKind::Nonexhaustive: { + auto *attr = cast(this); + Printer << "@nonexhaustive"; + switch (attr->getMode()) { + case NonexhaustiveMode::Error: + break; + case NonexhaustiveMode::Warning: + Printer << "(warn)"; + break; + } + break; + } + #define SIMPLE_DECL_ATTR(X, CLASS, ...) case DeclAttrKind::CLASS: #include "swift/AST/DeclAttr.def" llvm_unreachable("handled above"); @@ -1956,6 +1969,8 @@ StringRef DeclAttribute::getAttrName() const { } case DeclAttrKind::Lifetime: return cast(this)->isUnderscored() ? "_lifetime" : "lifetime"; + case DeclAttrKind::Nonexhaustive: + return "nonexhaustive"; } llvm_unreachable("bad DeclAttrKind"); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a87846e79ea15..a61992442c095 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7013,14 +7013,11 @@ bool EnumDecl::treatAsExhaustiveForDiags(const DeclContext *useDC) const { if (enumModule->inSamePackage(useDC->getParentModule())) return true; - // If the module where enum is declared supports extensible enumerations - // and this enum is not explicitly marked as "@frozen", cross-module - // access cannot be exhaustive and requires `@unknown default:`. - if (enumModule->supportsExtensibleEnums() && - !getAttrs().hasAttribute()) { - if (useDC != enumModule->getDeclContext()) - return false; - } + // When the enum is marked as `@nonexhaustive` cross-module access + // cannot be exhaustive and requires `@unknown default:`. + if (getAttrs().hasAttribute() && + enumModule != useDC->getParentModule()) + return false; } return isFormallyExhaustive(useDC); diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 7cbdba9d0141b..2e3796672df4b 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -124,7 +124,6 @@ UNINTERESTING_FEATURE(Volatile) UNINTERESTING_FEATURE(SuppressedAssociatedTypes) UNINTERESTING_FEATURE(StructLetDestructuring) UNINTERESTING_FEATURE(MacrosOnImports) -UNINTERESTING_FEATURE(ExtensibleEnums) UNINTERESTING_FEATURE(NonisolatedNonsendingByDefault) UNINTERESTING_FEATURE(KeyPathWithMethodMembers) UNINTERESTING_FEATURE(SendableProhibitsMainActorInference) @@ -657,6 +656,10 @@ static bool usesFeatureAsyncExecutionBehaviorAttributes(Decl *decl) { return false; } +static bool usesFeatureNonexhaustiveAttribute(Decl *decl) { + return decl->getAttrs().hasAttribute(); +} + UNINTERESTING_FEATURE(BuiltinSelect) static bool usesFeatureAlwaysInheritActorContext(Decl *decl) { diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 268b9f74d259f..5b686cf5f0aae 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -228,6 +228,7 @@ extension ASTGenVisitor { .dynamicCallable, .eagerMove, .exported, + .nonexhaustive, .discardableResult, .disfavoredOverload, .dynamicMemberLookup, diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 84b2a19c384ee..739b316023571 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -1501,8 +1501,6 @@ ModuleDecl *CompilerInstance::getMainModule() const { MainModule->setSerializePackageEnabled(); if (Invocation.getLangOptions().hasFeature(Feature::StrictMemorySafety)) MainModule->setStrictMemorySafety(true); - if (Invocation.getLangOptions().hasFeature(Feature::ExtensibleEnums)) - MainModule->setSupportsExtensibleEnums(true); configureAvailabilityDomains(getASTContext(), Invocation.getFrontendOptions(), MainModule); diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index 4752c56950af4..c635f1fb67840 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -3273,6 +3273,11 @@ void CompletionLookup::getAttributeDeclParamCompletions( getStoredPropertyCompletions(NT); } } + break; + } + case ParameterizedDeclAttributeKind::Nonexhaustive: { + addDeclAttrParamKeyword("warn", /*Parameters=*/{}, "", false); + break; } } } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 492fa70d7a206..a5bb1f45d0f32 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3970,6 +3970,21 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, Attributes.add(Attr.get()); break; } + + case DeclAttrKind::Nonexhaustive: { + auto mode = parseSingleAttrOption( + *this, Loc, AttrRange, AttrName, DK, + {{Context.Id_warn, NonexhaustiveMode::Warning}}, + NonexhaustiveMode::Error, + ParameterizedDeclAttributeKind::Nonexhaustive); + if (!mode) + return makeParserSuccess(); + + if (!DiscardAttribute) + Attributes.add(new (Context) NonexhaustiveAttr(AtLoc, AttrRange, *mode)); + + break; + } } if (DuplicateAttribute) { diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index d0cef2d54be9e..4d25920da3ecd 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -243,6 +243,21 @@ class AttributeChecker : public AttributeVisitor { } } + void visitNonexhaustiveAttr(NonexhaustiveAttr *attr) { + auto *E = cast(D); + + if (D->getAttrs().hasAttribute()) { + diagnoseAndRemoveAttr(attr, diag::nonexhaustive_attr_on_frozen_type); + return; + } + + if (E->getFormalAccess() < AccessLevel::Package) { + diagnoseAndRemoveAttr(attr, diag::nonexhaustive_attr_on_internal_type, + E->getName(), E->getFormalAccess()); + return; + } + } + void visitAlignmentAttr(AlignmentAttr *attr) { // Alignment must be a power of two. auto value = attr->getValue(); diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index e4bce4488a7f7..897fe2db9701f 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1616,6 +1616,7 @@ namespace { UNINTERESTING_ATTR(Isolated) UNINTERESTING_ATTR(Optimize) UNINTERESTING_ATTR(Exclusivity) + UNINTERESTING_ATTR(Nonexhaustive) UNINTERESTING_ATTR(NoLocks) UNINTERESTING_ATTR(NoAllocation) UNINTERESTING_ATTR(NoRuntime) diff --git a/lib/Sema/TypeCheckSwitchStmt.cpp b/lib/Sema/TypeCheckSwitchStmt.cpp index 5e5faf5d0b3e9..2dcc2b59297df 100644 --- a/lib/Sema/TypeCheckSwitchStmt.cpp +++ b/lib/Sema/TypeCheckSwitchStmt.cpp @@ -1154,19 +1154,34 @@ namespace { assert(defaultReason == RequiresDefault::No); Type subjectType = Switch->getSubjectExpr()->getType(); bool shouldIncludeFutureVersionComment = false; - bool shouldDowngradeToWarning = true; - if (auto *theEnum = subjectType->getEnumOrBoundGenericEnum()) { + auto *theEnum = subjectType->getEnumOrBoundGenericEnum(); + + if (theEnum) { auto *enumModule = theEnum->getParentModule(); shouldIncludeFutureVersionComment = enumModule->isSystemModule() || - enumModule->supportsExtensibleEnums(); - // Since the module enabled `ExtensibleEnums` feature they - // opted-in all of their clients into exhaustivity errors. - shouldDowngradeToWarning = !enumModule->supportsExtensibleEnums(); + theEnum->getAttrs().hasAttribute(); } - DE.diagnose(startLoc, diag::non_exhaustive_switch_unknown_only, - subjectType, shouldIncludeFutureVersionComment) - .warnUntilSwiftVersionIf(shouldDowngradeToWarning, 6); + + auto diag = + DE.diagnose(startLoc, diag::non_exhaustive_switch_unknown_only, + subjectType, shouldIncludeFutureVersionComment); + + auto shouldWarnUntilVersion = [&theEnum]() -> unsigned { + if (theEnum) { + // Presence of `@nonexhaustive(warn)` pushes the warning farther, + // into the future. + if (auto *nonexhaustive = + theEnum->getAttrs().getAttribute()) { + if (nonexhaustive->getMode() == NonexhaustiveMode::Warning) + return swift::version::Version::getFutureMajorLanguageVersion(); + } + } + return 6; + }; + + diag.warnUntilSwiftVersion(shouldWarnUntilVersion()); + mainDiagType = std::nullopt; } break; diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index ab03112fb6752..b9b8d5cd5828d 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -6648,6 +6648,14 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { break; } + case decls_block::Nonexhaustive_DECL_ATTR: { + unsigned mode; + serialization::decls_block::NonexhaustiveDeclAttrLayout::readRecord( + scratch, mode); + Attr = new (ctx) NonexhaustiveAttr((NonexhaustiveMode)mode); + break; + } + #define SIMPLE_DECL_ATTR(NAME, CLASS, ...) \ case decls_block::CLASS##_DECL_ATTR: { \ bool isImplicit; \ diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 6839f808c7463..6bd277c8839b2 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -714,11 +714,6 @@ class ModuleFile /// \c true if this module was built with strict memory safety. bool strictMemorySafety() const { return Core->strictMemorySafety(); } - /// \c true if this module was built with `ExtensibleEnums` feature enabled. - bool supportsExtensibleEnums() const { - return Core->supportsExtensibleEnums(); - } - /// Associates this module file with the AST node representing it. /// /// Checks that the file is compatible with the AST module it's being loaded diff --git a/lib/Serialization/ModuleFileSharedCore.cpp b/lib/Serialization/ModuleFileSharedCore.cpp index e3fd7812c2046..f62a28871978f 100644 --- a/lib/Serialization/ModuleFileSharedCore.cpp +++ b/lib/Serialization/ModuleFileSharedCore.cpp @@ -225,9 +225,6 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor, case options_block::STRICT_MEMORY_SAFETY: extendedInfo.setStrictMemorySafety(true); break; - case options_block::EXTENSIBLE_ENUMS: - extendedInfo.setSupportsExtensibleEnums(true); - break; default: // Unknown options record, possibly for use by a future version of the // module format. @@ -1507,7 +1504,6 @@ ModuleFileSharedCore::ModuleFileSharedCore( Bits.AllowNonResilientAccess = extInfo.allowNonResilientAccess(); Bits.SerializePackageEnabled = extInfo.serializePackageEnabled(); Bits.StrictMemorySafety = extInfo.strictMemorySafety(); - Bits.SupportsExtensibleEnums = extInfo.supportsExtensibleEnums(); MiscVersion = info.miscVersion; SDKVersion = info.sdkVersion; ModuleABIName = extInfo.getModuleABIName(); diff --git a/lib/Serialization/ModuleFileSharedCore.h b/lib/Serialization/ModuleFileSharedCore.h index f63578c5b6381..b9f00f2680b0a 100644 --- a/lib/Serialization/ModuleFileSharedCore.h +++ b/lib/Serialization/ModuleFileSharedCore.h @@ -421,11 +421,8 @@ class ModuleFileSharedCore { /// Whether this module enabled strict memory safety. unsigned StrictMemorySafety : 1; - /// Whether this module enabled has `ExtensibleEnums` feature enabled. - unsigned SupportsExtensibleEnums : 1; - // Explicitly pad out to the next word boundary. - unsigned : 1; + unsigned : 2; } Bits = {}; static_assert(sizeof(ModuleBits) <= 8, "The bit set should be small"); @@ -688,8 +685,6 @@ class ModuleFileSharedCore { bool strictMemorySafety() const { return Bits.StrictMemorySafety; } - bool supportsExtensibleEnums() const { return Bits.SupportsExtensibleEnums; } - /// How should \p dependency be loaded for a transitive import via \c this? /// /// If \p importNonPublicDependencies, more transitive dependencies diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 7ceb534d89d1b..9ba8afff78b14 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 943; // Lifetime dependencies on enum element +const uint16_t SWIFTMODULE_VERSION_MINOR = 946; // @preEnumExtensibility attribute /// A standard hash seed used for all string hashes in a serialized module. /// @@ -986,8 +986,7 @@ namespace options_block { CXX_STDLIB_KIND, PUBLIC_MODULE_NAME, SWIFT_INTERFACE_COMPILER_VERSION, - STRICT_MEMORY_SAFETY, - EXTENSIBLE_ENUMS, + STRICT_MEMORY_SAFETY }; using SDKPathLayout = BCRecordLayout< @@ -1097,10 +1096,6 @@ namespace options_block { SWIFT_INTERFACE_COMPILER_VERSION, BCBlob // version tuple >; - - using ExtensibleEnumsLayout = BCRecordLayout< - EXTENSIBLE_ENUMS - >; } /// The record types within the input block. @@ -2587,6 +2582,11 @@ namespace decls_block { BCArray> // concatenated param indices >; + using NonexhaustiveDeclAttrLayout = BCRecordLayout< + Nonexhaustive_DECL_ATTR, + BCFixed<2> // mode + >; + // clang-format on #undef SYNTAX_SUGAR_TYPE_LAYOUT diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index b4b1c6f6bbf97..fa1869a918ab2 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -1187,11 +1187,6 @@ void Serializer::writeHeader() { static_cast(M->getCXXStdlibKind())); } - if (M->supportsExtensibleEnums()) { - options_block::ExtensibleEnumsLayout ExtensibleEnums(Out); - ExtensibleEnums.emit(ScratchRecord); - } - if (Options.SerializeOptionsForDebugging) { options_block::SDKPathLayout SDKPath(Out); options_block::XCCLayout XCC(Out); @@ -3567,6 +3562,13 @@ class Serializer::DeclSerializer : public DeclVisitor { case DeclAttrKind::Lifetime: { return; } + case DeclAttrKind::Nonexhaustive: { + auto *theAttr = cast(DA); + auto abbrCode = S.DeclTypeAbbrCodes[NonexhaustiveDeclAttrLayout::Code]; + NonexhaustiveDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, + (unsigned)theAttr->getMode()); + return; + } } } diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index a72140158bb45..e2389b805bc27 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -1083,8 +1083,6 @@ LoadedFile *SerializedModuleLoaderBase::loadAST( if (!loadedModuleFile->getModulePackageName().empty()) { M.setPackageName(Ctx.getIdentifier(loadedModuleFile->getModulePackageName())); } - if (loadedModuleFile->supportsExtensibleEnums()) - M.setSupportsExtensibleEnums(); M.setUserModuleVersion(loadedModuleFile->getUserModuleVersion()); M.setSwiftInterfaceCompilerVersion( loadedModuleFile->getSwiftInterfaceCompilerVersion()); diff --git a/test/IDE/complete_decl_attribute_feature_requirement.swift b/test/IDE/complete_decl_attribute_feature_requirement.swift index 461666a5e1f2f..1400b872670da 100644 --- a/test/IDE/complete_decl_attribute_feature_requirement.swift +++ b/test/IDE/complete_decl_attribute_feature_requirement.swift @@ -3,136 +3,18 @@ // it's enabled. When a feature becomes non-experimental, move its test cases // into the normal complete_decl_attribute.swift test file. -// NOTE: There are currently no experimental features that need code completion -// testing, but this test file is being left in place for when it's needed -// again. At that time, please remove the ABIAttribute tests. -// REQUIRES: new_use_case - // REQUIRES: asserts // RUN: %batch-code-completion -filecheck-additional-suffix _DISABLED // RUN: %batch-code-completion -filecheck-additional-suffix _ENABLED \ -// RUN: -enable-experimental-feature ABIAttribute +// RUN: -enable-experimental-feature NonexhaustiveAttribute // NOTE: Please do not include the ", N items" after "Begin completions". The // item count creates needless merge conflicts given that an "End completions" // line exists for each test. -@#^KEYWORD2^# func method(){} - -// KEYWORD2: Begin completions -// KEYWORD2_ENABLED-DAG: Keyword/None: abi[#Func Attribute#]; name=abi -// KEYWORD2_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD2: End completions - -@#^KEYWORD3^# class C {} - -// KEYWORD3: Begin completions -// KEYWORD3_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD3_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD3: End completions - -@#^KEYWORD3_2?check=KEYWORD3^#IB class C2 {} -// Same as KEYWORD3. - @#^KEYWORD4^# enum E {} // KEYWORD4: Begin completions -// KEYWORD4_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD4_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// KEYWORD4_ENABLED-DAG: Keyword/None: nonexhaustive[#{{.*}} Attribute#]; name=nonexhaustive +// KEYWORD4_DISABLED-NOT: Keyword/None: nonexhaustive[#{{.*}} Attribute#]; name=nonexhaustive // KEYWORD4: End completions - -@#^KEYWORD5^# struct S{} -// KEYWORD5: Begin completions -// KEYWORD5_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD5_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// KEYWORD5: End completions - -@#^ON_GLOBALVAR^# var globalVar -// ON_GLOBALVAR: Begin completions -// ON_GLOBALVAR_ENABLED-DAG: Keyword/None: abi[#Var Attribute#]; name=abi -// ON_GLOBALVAR_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_GLOBALVAR: End completions - -struct _S { - @#^ON_INIT^# init() -// ON_INIT: Begin completions -// ON_INIT_ENABLED-DAG: Keyword/None: abi[#Constructor Attribute#]; name=abi -// ON_INIT_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_INIT: End completions - - @#^ON_PROPERTY^# var foo -// ON_PROPERTY: Begin completions -// ON_PROPERTY_ENABLED-DAG: Keyword/None: abi[#Var Attribute#]; name=abi -// ON_PROPERTY_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_PROPERTY: End completions - - @#^ON_SUBSCR^# subscript -// ON_SUBSCR: Begin completions -// ON_SUBSCR_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi -// ON_SUBSCR_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_SUBSCR: End completions - - @#^ON_METHOD^# private - func foo() -// ON_METHOD: Begin completions -// ON_METHOD_ENABLED-DAG: Keyword/None: abi[#Func Attribute#]; name=abi -// ON_METHOD_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_METHOD: End completions - - - func bar(@#^ON_PARAM_1?check=ON_PARAM^#) -// ON_PARAM: Begin completions -// ON_PARAM_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_PARAM_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_PARAM: End completions - - func bar( - @#^ON_PARAM_2?check=ON_PARAM^# - - arg: Int - ) -// Same as ON_PARAM. - - @#^ON_MEMBER_INDEPENDENT_1?check=ON_MEMBER_LAST^# - - func dummy1() {} -// Same as ON_MEMBER_LAST. - - @#^ON_MEMBER_INDEPENDENT_2?check=ON_MEMBER_LAST^# - func dummy2() {} -// Same as ON_MEMBER_LAST. - - - @#^ON_MEMBER_LAST^# -// ON_MEMBER_LAST: Begin completions -// ON_MEMBER_LAST_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi -// ON_MEMBER_LAST_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// ON_MEMBER_LAST: End completions -} - -func takeClosure(_: () -> Void) { - takeClosure { @#^IN_CLOSURE^# in - print("x") - } -} -// IN_CLOSURE: Begin completions -// FIXME: Not valid in this position (but CompletionLookup can't tell that) -// IN_CLOSURE_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi -// IN_CLOSURE_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi -// IN_CLOSURE: End completions - -@#^KEYWORD_INDEPENDENT_1?check=KEYWORD_LAST^# - -func dummy1() {} -// Same as KEYWORD_LAST. - -@#^KEYWORD_INDEPENDENT_2?check=KEYWORD_LAST^# -func dummy2() {} -// Same as KEYWORD_LAST. - -@#^KEYWORD_LAST^# - -// KEYWORD_LAST: Begin completions -// KEYWORD_LAST_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi -// KEYWORD_LAST_DISABLED-NOT: Keyword/None: abi[#Declaration Attribute#]; name=abi -// KEYWORD_LAST: End completions diff --git a/test/IDE/complete_nonexhaustive.swift b/test/IDE/complete_nonexhaustive.swift new file mode 100644 index 0000000000000..003bce451db02 --- /dev/null +++ b/test/IDE/complete_nonexhaustive.swift @@ -0,0 +1,9 @@ +// RUN: %batch-code-completion -enable-experimental-feature NonexhaustiveAttribute + +// REQUIRES: swift_feature_NonexhaustiveAttribute + +// NONEXHAUSTIVE-DAG: Keyword/None: warn; name=warn + +@nonexhaustive(#^NONEXHAUSTIVE^#) +public enum E { +} diff --git a/test/ModuleInterface/nonexhaustive_attr.swift b/test/ModuleInterface/nonexhaustive_attr.swift new file mode 100644 index 0000000000000..c4151e36d5177 --- /dev/null +++ b/test/ModuleInterface/nonexhaustive_attr.swift @@ -0,0 +1,28 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-emit-module-interface(%t/Library.swiftinterface) %s -enable-experimental-feature NonexhaustiveAttribute -module-name Library +// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -enable-experimental-feature NonexhaustiveAttribute -module-name Library +// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -module-name Library +// RUN: %FileCheck %s < %t/Library.swiftinterface + +// REQUIRES: swift_feature_NonexhaustiveAttribute + +// CHECK: #if compiler(>=5.3) && $NonexhaustiveAttribute +// CHECK-NEXT: @nonexhaustive public enum E { +// CHECK-NEXT: } +// CHECK-NEXT: #else +// CHECK-NEXT: public enum E { +// CHECK-NEXT: } +// CHECK-NEXT: #endif +@nonexhaustive +public enum E { +} + +// CHECK: #if compiler(>=5.3) && $NonexhaustiveAttribute +// CHECK-NEXT: @nonexhaustive(warn) public enum F { +// CHECK: #else +// CHECK-NEXT: public enum F { +// CHECK: #endif +@nonexhaustive(warn) +public enum F { + case a +} diff --git a/test/ModuleInterface/extensible_enums.swift b/test/ModuleInterface/nonexhaustive_enums.swift similarity index 59% rename from test/ModuleInterface/extensible_enums.swift rename to test/ModuleInterface/nonexhaustive_enums.swift index 8f3d985a32ab6..7f28cd5202187 100644 --- a/test/ModuleInterface/extensible_enums.swift +++ b/test/ModuleInterface/nonexhaustive_enums.swift @@ -6,9 +6,9 @@ // RUN: %target-swift-frontend -emit-module %t/src/Lib.swift \ // RUN: -module-name Lib \ // RUN: -emit-module-path %t/Lib.swiftmodule \ -// RUN: -enable-experimental-feature ExtensibleEnums +// RUN: -enable-experimental-feature NonexhaustiveAttribute -// Check that the errors are produced when using enums from module with `ExtensibleEnums` feature enabled. +// Check that the errors are produced when using enums from module with `NonexhaustiveEnums` feature enabled. // RUN: %target-swift-frontend -typecheck %t/src/TestChecking.swift \ // RUN: -swift-version 5 -module-name Client -I %t \ // RUN: -verify @@ -20,7 +20,8 @@ // RUN: -module-name Lib \ // RUN: -package-name Test \ // RUN: -emit-module-path %t/Lib.swiftmodule \ -// RUN: -enable-experimental-feature ExtensibleEnums +// RUN: -enable-experimental-feature NonexhaustiveAttribute + // Different module but the same package // RUN: %target-swift-frontend -typecheck %t/src/TestSamePackage.swift \ @@ -28,14 +29,25 @@ // RUN: -package-name Test \ // RUN: -verify -// REQUIRES: swift_feature_ExtensibleEnums +// Different module but the same package +// RUN: %target-swift-frontend -typecheck %t/src/TestSwift6.swift \ +// RUN: -swift-version 6 -module-name Client -I %t \ +// RUN: -verify + +// REQUIRES: swift_feature_NonexhaustiveAttribute //--- Lib.swift +@nonexhaustive public enum E { case a } +@nonexhaustive(warn) +public enum PE { + case a +} + @frozen public enum F { case a @@ -56,11 +68,11 @@ func test_same_module(e: E, f: F) { //--- TestChecking.swift import Lib -func test(e: E, f: F) { - // `E` is not marked as `@frozen` which means it gets new semantics +func test(e: E, pe: PE, f: F) { + // `E` is marked as `@nonexhaustive` which means it gets new semantics switch e { - // expected-error@-1 {{switch covers known cases, but 'E' may have additional unknown values, possibly added in future versions}} + // 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}} // expected-note@-2 {{handle unknown values using "@unknown default"}} case .a: break } @@ -70,7 +82,13 @@ func test(e: E, f: F) { @unknown default: break } - // `F` is marked as `@frozen` which means regular rules apply even with `ExtensibleEnums` feature enabled. + switch pe { + // expected-warning@-1 {{switch covers known cases, but 'PE' may have additional unknown values, possibly added in future versions; this will be an error in a future Swift language mode}} + // expected-note@-2 {{handle unknown values using "@unknown default"}} + case .a: break + } + + // `F` is marked as `@frozen` which means regular rules apply. switch f { // Ok (no errors because `F` is `@frozen`) case .a: break @@ -108,3 +126,25 @@ func test_no_default(e: E, f: F) { @unknown default: break } } + +//--- TestSwift6.swift +import Lib + +func test(e: E, pe: PE, f: F) { + switch e { + // expected-error@-1 {{switch covers known cases, but 'E' may have additional unknown values, possibly added in future versions}} + // expected-note@-2 {{handle unknown values using "@unknown default"}} + case .a: break + } + + switch e { // Ok (no warnings) + case .a: break + @unknown default: break + } + + switch pe { + // expected-warning@-1 {{switch covers known cases, but 'PE' may have additional unknown values, possibly added in future versions; this will be an error in a future Swift language mode}} + // expected-note@-2 {{handle unknown values using "@unknown default"}} + case .a: break + } +} diff --git a/test/attr/attr_nonexhaustive.swift b/test/attr/attr_nonexhaustive.swift new file mode 100644 index 0000000000000..48dfa19270efc --- /dev/null +++ b/test/attr/attr_nonexhaustive.swift @@ -0,0 +1,49 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-feature NonexhaustiveAttribute + +// REQUIRES: swift_feature_NonexhaustiveAttribute + +@nonexhaustive +public enum E1 { // Ok +} + +@nonexhaustive // expected-error {{'@nonexhaustive' attribute can only be applied to public or package declarations, but 'E2' is fileprivate}} +fileprivate enum E2 {} + +@nonexhaustive // expected-error {{cannot use '@nonexhaustive' together with '@frozen'}} +@frozen +public enum E3 { +} + +@nonexhaustive // expected-error {{'@nonexhaustive' attribute can only be applied to public or package declarations, but 'E4' is internal}} +@usableFromInline +enum E4 {} + +@nonexhaustive // expected-error {{@nonexhaustive may only be used on 'enum' declarations}} +struct Test { + @nonexhaustive // expected-error {{@nonexhaustive may only be used on 'enum' declarations}} + var v: Int { + @nonexhaustive // expected-error {{@nonexhaustive may only be used on 'enum' declarations}} + get { 0 } + } + + @nonexhaustive // expected-error {{@nonexhaustive may only be used on 'enum' declarations}} + var v2: String = "" + + @nonexhaustive // expected-error {{@nonexhaustive may only be used on 'enum' declarations}} + func test() {} + + @nonexhaustive // expected-error {{@nonexhaustive may only be used on 'enum' declarations}} + subscript(a: Int) -> Bool { + get { false } + set { } + } +} + +@nonexhaustive(warn) +public enum PE { +} + +@nonexhaustive // expected-note {{attribute already specified here}} +@nonexhaustive(warn) // expected-error {{duplicate attribute}} +public enum WrongPreE { +} diff --git a/test/attr/feature_requirement.swift b/test/attr/feature_requirement.swift index d8e6121311bfc..7e5ee44123f2f 100644 --- a/test/attr/feature_requirement.swift +++ b/test/attr/feature_requirement.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -parse-as-library -disable-experimental-parser-round-trip -verify-additional-prefix disabled- -// RUN: %target-typecheck-verify-swift -parse-as-library -verify-additional-prefix enabled- -enable-experimental-feature CompileTimeValues +// RUN: %target-typecheck-verify-swift -parse-as-library -verify-additional-prefix enabled- -enable-experimental-feature CompileTimeValues -enable-experimental-feature NonexhaustiveAttribute // REQUIRES: asserts @@ -14,3 +14,13 @@ public let x = 1 // expected-disabled-error@-1 {{'const' attribute is only vali #else #error("doesn't have @const") // expected-disabled-error {{doesn't have @const}} #endif + +@nonexhaustive +public enum E {} // expected-disabled-error@-1 {{'nonexhaustive' attribute is only valid when experimental feature NonexhaustiveAttribute is enabled}} + +#if hasAttribute(nonexhaustive) + #error("does have @nonexhaustive") // expected-enabled-error {{does have @nonexhaustive}} +#else + #error("doesn't have @nonexhaustive") // expected-disabled-error {{doesn't have @nonexhaustive}} +#endif +