From fe1ae7571197d67afe06032c9788a0cc097db9d5 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 1 Jul 2025 14:21:49 -0700 Subject: [PATCH 1/3] [AST/Sema] SE-0487: Rename `@extensible` into `@nonexhaustive` This is an accepted spelling for the attribute. This commit also renames the feature flag from `ExtensibleAttribute` to `NonexhaustiveAttribute` to match the spelling of the attribute. --- include/swift/AST/DeclAttr.def | 6 +-- include/swift/AST/DiagnosticsSema.def | 14 +++--- include/swift/Basic/Features.def | 4 +- lib/AST/ASTDumper.cpp | 2 +- lib/AST/ASTPrinter.cpp | 6 +-- lib/AST/Decl.cpp | 4 +- lib/AST/FeatureSet.cpp | 4 +- lib/ASTGen/Sources/ASTGen/DeclAttrs.swift | 2 +- lib/Sema/TypeCheckAttr.cpp | 10 ++-- lib/Sema/TypeCheckDeclOverride.cpp | 2 +- lib/Sema/TypeCheckSwitchStmt.cpp | 2 +- ...le_attr.swift => nonexhaustive_attr.swift} | 18 +++---- ..._enums.swift => nonexhaustive_enums.swift} | 14 +++--- test/attr/attr_extensible.swift | 49 ------------------- test/attr/attr_nonexhaustive.swift | 49 +++++++++++++++++++ test/attr/feature_requirement.swift | 12 ++--- 16 files changed, 99 insertions(+), 99 deletions(-) rename test/ModuleInterface/{extensible_attr.swift => nonexhaustive_attr.swift} (53%) rename test/ModuleInterface/{extensible_enums.swift => nonexhaustive_enums.swift} (92%) delete mode 100644 test/attr/attr_extensible.swift create mode 100644 test/attr/attr_nonexhaustive.swift diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index a7d827a837b8c..f0a0949c945b4 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -877,11 +877,11 @@ SIMPLE_DECL_ATTR(constInitialized, ConstInitialized, 168) DECL_ATTR_FEATURE_REQUIREMENT(ConstInitialized, CompileTimeValues) -SIMPLE_DECL_ATTR(extensible, Extensible, +SIMPLE_DECL_ATTR(nonexhaustive, Nonexhaustive, OnEnum, ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | ForbiddenInABIAttr, 169) -DECL_ATTR_FEATURE_REQUIREMENT(Extensible, ExtensibleAttribute) +DECL_ATTR_FEATURE_REQUIREMENT(Nonexhaustive, NonexhaustiveAttribute) SIMPLE_DECL_ATTR(concurrent, Concurrent, OnFunc | OnConstructor | OnSubscript | OnVar, @@ -892,7 +892,7 @@ SIMPLE_DECL_ATTR(preEnumExtensibility, PreEnumExtensibility, OnEnum, ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 171) -DECL_ATTR_FEATURE_REQUIREMENT(PreEnumExtensibility, ExtensibleAttribute) +DECL_ATTR_FEATURE_REQUIREMENT(PreEnumExtensibility, NonexhaustiveAttribute) DECL_ATTR(specialized, Specialized, OnConstructor | OnFunc | OnAccessor, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 4b1f41ca64aab..ea16193d2a78a 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8799,20 +8799,20 @@ GROUPED_WARNING( (StringRef)) //===----------------------------------------------------------------------===// -// MARK: @extensible and @preEnumExtensibility Attributes +// MARK: @nonexhaustive and @preEnumExtensibility Attributes //===----------------------------------------------------------------------===// -ERROR(extensible_attr_on_frozen_type,none, - "cannot use '@extensible' together with '@frozen'", ()) +ERROR(nonexhaustive_attr_on_frozen_type,none, + "cannot use '@nonexhaustive' together with '@frozen'", ()) -ERROR(extensible_attr_on_internal_type,none, - "'@extensible' attribute can only be applied to public or package " +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)) -ERROR(pre_enum_extensibility_without_extensible,none, - "%0 can only be used together with '@extensible' attribute", (DeclAttribute)) +ERROR(pre_enum_extensibility_without_nonexhaustive,none, + "%0 can only be used together with '@nonexhaustive' attribute", (DeclAttribute)) //===----------------------------------------------------------------------===// // MARK: `using` declaration diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index d2b5b5266a34b..f7f70e8eecc74 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -519,8 +519,8 @@ EXPERIMENTAL_FEATURE(AllowRuntimeSymbolDeclarations, true) /// Allow use of `@cdecl` EXPERIMENTAL_FEATURE(CDecl, false) -/// Allow use of `@extensible` on public enums -SUPPRESSIBLE_EXPERIMENTAL_FEATURE(ExtensibleAttribute, false) +/// Allow use of `@nonexhaustive` on public enums +SUPPRESSIBLE_EXPERIMENTAL_FEATURE(NonexhaustiveAttribute, false) /// Allow use of `Module::name` syntax EXPERIMENTAL_FEATURE(ModuleSelector, false) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index d36bfdf9b1aa0..2aaf1af0f07f1 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -5047,7 +5047,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(Extensible, extensible) + TRIVIAL_ATTR_PRINTER(Nonexhaustive, nonexhaustive) TRIVIAL_ATTR_PRINTER(Concurrent, concurrent) TRIVIAL_ATTR_PRINTER(PreEnumExtensibility, preEnumExtensibility) diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 81ae267421721..c191a4fbfad1e 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3325,9 +3325,9 @@ suppressingFeatureAddressableTypes(PrintOptions &options, } static void -suppressingFeatureExtensibleAttribute(PrintOptions &options, - llvm::function_ref action) { - ExcludeAttrRAII scope1(options.ExcludeAttrList, DeclAttrKind::Extensible); +suppressingFeatureNonexhaustiveAttribute(PrintOptions &options, + llvm::function_ref action) { + ExcludeAttrRAII scope1(options.ExcludeAttrList, DeclAttrKind::Nonexhaustive); ExcludeAttrRAII scope2(options.ExcludeAttrList, DeclAttrKind::PreEnumExtensibility); action(); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 2ba6baf3a7461..6a67aae895d0d 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7035,9 +7035,9 @@ bool EnumDecl::treatAsExhaustiveForDiags(const DeclContext *useDC) const { if (enumModule->inSamePackage(useDC->getParentModule())) return true; - // When the enum is marked as `@extensible` cross-module access + // When the enum is marked as `@nonexhaustive` cross-module access // cannot be exhaustive and requires `@unknown default:`. - if (getAttrs().hasAttribute() && + if (getAttrs().hasAttribute() && enumModule != useDC->getParentModule()) return false; } diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 9c0b7ac8200e5..4c603336de472 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -667,8 +667,8 @@ static bool usesFeatureAsyncExecutionBehaviorAttributes(Decl *decl) { return false; } -static bool usesFeatureExtensibleAttribute(Decl *decl) { - return decl->getAttrs().hasAttribute(); +static bool usesFeatureNonexhaustiveAttribute(Decl *decl) { + return decl->getAttrs().hasAttribute(); } static bool usesFeatureAlwaysInheritActorContext(Decl *decl) { diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 962ffe5b6487b..0d4a7b3481c19 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -239,7 +239,7 @@ extension ASTGenVisitor { .DynamicCallable, .EagerMove, .Exported, - .Extensible, + .Nonexhaustive, .PreEnumExtensibility, .DiscardableResult, .DisfavoredOverload, diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 72823e718a73b..09f98d34056e4 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -233,25 +233,25 @@ class AttributeChecker : public AttributeVisitor { } } - void visitExtensibleAttr(ExtensibleAttr *attr) { + void visitNonexhaustiveAttr(NonexhaustiveAttr *attr) { auto *E = cast(D); if (D->getAttrs().hasAttribute()) { - diagnoseAndRemoveAttr(attr, diag::extensible_attr_on_frozen_type); + diagnoseAndRemoveAttr(attr, diag::nonexhaustive_attr_on_frozen_type); return; } if (E->getFormalAccess() < AccessLevel::Package) { - diagnoseAndRemoveAttr(attr, diag::extensible_attr_on_internal_type, + diagnoseAndRemoveAttr(attr, diag::nonexhaustive_attr_on_internal_type, E->getName(), E->getFormalAccess()); return; } } void visitPreEnumExtensibilityAttr(PreEnumExtensibilityAttr *attr) { - if (!D->getAttrs().hasAttribute()) { + if (!D->getAttrs().hasAttribute()) { diagnoseAndRemoveAttr( - attr, diag::pre_enum_extensibility_without_extensible, attr); + attr, diag::pre_enum_extensibility_without_nonexhaustive, attr); return; } } diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index c2b9d89d69df1..b998e24f58b8e 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1614,7 +1614,7 @@ namespace { UNINTERESTING_ATTR(Isolated) UNINTERESTING_ATTR(Optimize) UNINTERESTING_ATTR(Exclusivity) - UNINTERESTING_ATTR(Extensible) + UNINTERESTING_ATTR(Nonexhaustive) UNINTERESTING_ATTR(PreEnumExtensibility) UNINTERESTING_ATTR(NoLocks) UNINTERESTING_ATTR(NoAllocation) diff --git a/lib/Sema/TypeCheckSwitchStmt.cpp b/lib/Sema/TypeCheckSwitchStmt.cpp index e537bad054781..41ebaf200de62 100644 --- a/lib/Sema/TypeCheckSwitchStmt.cpp +++ b/lib/Sema/TypeCheckSwitchStmt.cpp @@ -1160,7 +1160,7 @@ namespace { auto *enumModule = theEnum->getParentModule(); shouldIncludeFutureVersionComment = enumModule->isSystemModule() || - theEnum->getAttrs().hasAttribute(); + theEnum->getAttrs().hasAttribute(); } auto diag = diff --git a/test/ModuleInterface/extensible_attr.swift b/test/ModuleInterface/nonexhaustive_attr.swift similarity index 53% rename from test/ModuleInterface/extensible_attr.swift rename to test/ModuleInterface/nonexhaustive_attr.swift index 0137ebb2c8a38..f918662375631 100644 --- a/test/ModuleInterface/extensible_attr.swift +++ b/test/ModuleInterface/nonexhaustive_attr.swift @@ -1,29 +1,29 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-emit-module-interface(%t/Library.swiftinterface) %s -enable-experimental-feature ExtensibleAttribute -module-name Library -// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -enable-experimental-feature ExtensibleAttribute -module-name Library +// 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_ExtensibleAttribute +// REQUIRES: swift_feature_NonexhaustiveAttribute -// CHECK: #if compiler(>=5.3) && $ExtensibleAttribute -// CHECK-NEXT: @extensible public enum E { +// 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 -@extensible +@nonexhaustive public enum E { } -// CHECK: #if compiler(>=5.3) && $ExtensibleAttribute -// CHECK-NEXT: @preEnumExtensibility @extensible public enum F { +// CHECK: #if compiler(>=5.3) && $NonexhaustiveAttribute +// CHECK-NEXT: @preEnumExtensibility @nonexhaustive public enum F { // CHECK: #else // CHECK-NEXT: public enum F { // CHECK: #endif @preEnumExtensibility -@extensible +@nonexhaustive public enum F { case a } diff --git a/test/ModuleInterface/extensible_enums.swift b/test/ModuleInterface/nonexhaustive_enums.swift similarity index 92% rename from test/ModuleInterface/extensible_enums.swift rename to test/ModuleInterface/nonexhaustive_enums.swift index 81dc581b84892..bae996ba72a87 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 ExtensibleAttribute +// 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,7 @@ // RUN: -module-name Lib \ // RUN: -package-name Test \ // RUN: -emit-module-path %t/Lib.swiftmodule \ -// RUN: -enable-experimental-feature ExtensibleAttribute +// RUN: -enable-experimental-feature NonexhaustiveAttribute // Different module but the same package @@ -34,17 +34,17 @@ // RUN: -swift-version 6 -module-name Client -I %t \ // RUN: -verify -// REQUIRES: swift_feature_ExtensibleAttribute +// REQUIRES: swift_feature_NonexhaustiveAttribute //--- Lib.swift -@extensible +@nonexhaustive public enum E { case a } @preEnumExtensibility -@extensible +@nonexhaustive public enum PE { case a } @@ -70,7 +70,7 @@ func test_same_module(e: E, f: F) { import Lib func test(e: E, pe: PE, f: F) { - // `E` is marked as `@extensible` which means it gets new semantics + // `E` is marked as `@nonexhaustive` which means it gets new semantics switch e { // 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}} diff --git a/test/attr/attr_extensible.swift b/test/attr/attr_extensible.swift deleted file mode 100644 index 7f7c3faf264ff..0000000000000 --- a/test/attr/attr_extensible.swift +++ /dev/null @@ -1,49 +0,0 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-feature ExtensibleAttribute - -// REQUIRES: swift_feature_ExtensibleAttribute - -@extensible -public enum E1 { // Ok -} - -@extensible // expected-error {{'@extensible' attribute can only be applied to public or package declarations, but 'E2' is fileprivate}} -fileprivate enum E2 {} - -@extensible // expected-error {{cannot use '@extensible' together with '@frozen'}} -@frozen -public enum E3 { -} - -@extensible // expected-error {{'@extensible' attribute can only be applied to public or package declarations, but 'E4' is internal}} -@usableFromInline -enum E4 {} - -@extensible // expected-error {{@extensible may only be used on 'enum' declarations}} -struct Test { - @extensible // expected-error {{@extensible may only be used on 'enum' declarations}} - var v: Int { - @extensible // expected-error {{@extensible may only be used on 'enum' declarations}} - get { 0 } - } - - @extensible // expected-error {{@extensible may only be used on 'enum' declarations}} - var v2: String = "" - - @extensible // expected-error {{@extensible may only be used on 'enum' declarations}} - func test() {} - - @extensible // expected-error {{@extensible may only be used on 'enum' declarations}} - subscript(a: Int) -> Bool { - get { false } - set { } - } -} - -@preEnumExtensibility -@extensible -public enum PE { -} - -@preEnumExtensibility // expected-error {{@preEnumExtensibility can only be used together with '@extensible' attribute}} -public enum WrongPreE { -} diff --git a/test/attr/attr_nonexhaustive.swift b/test/attr/attr_nonexhaustive.swift new file mode 100644 index 0000000000000..1ba9761dafb16 --- /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 { } + } +} + +@preEnumExtensibility +@nonexhaustive +public enum PE { +} + +@preEnumExtensibility // expected-error {{@preEnumExtensibility can only be used together with '@nonexhaustive' attribute}} +public enum WrongPreE { +} diff --git a/test/attr/feature_requirement.swift b/test/attr/feature_requirement.swift index eb127063d5a00..b69c42026340f 100644 --- a/test/attr/feature_requirement.swift +++ b/test/attr/feature_requirement.swift @@ -1,17 +1,17 @@ // 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 ExtensibleAttribute +// RUN: %target-typecheck-verify-swift -parse-as-library -verify-additional-prefix enabled- -enable-experimental-feature NonexhaustiveAttribute // REQUIRES: asserts // This test checks whether DECL_ATTR_FEATURE_REQUIREMENT is being applied correctly. // It is expected to need occasional edits as experimental features are stabilized. -@extensible -public enum E {} // expected-disabled-error@-1 {{'extensible' attribute is only valid when experimental feature ExtensibleAttribute is enabled}} +@nonexhaustive +public enum E {} // expected-disabled-error@-1 {{'nonexhaustive' attribute is only valid when experimental feature NonexhaustiveAttribute is enabled}} -#if hasAttribute(extensible) - #error("does have @extensible") // expected-enabled-error {{does have @extensible}} +#if hasAttribute(nonexhaustive) + #error("does have @nonexhaustive") // expected-enabled-error {{does have @nonexhaustive}} #else - #error("doesn't have @extensible") // expected-disabled-error {{doesn't have @extensible}} + #error("doesn't have @nonexhaustive") // expected-disabled-error {{doesn't have @nonexhaustive}} #endif From 43eec8fede049e3073748be421fcf4207c1e4135 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 4 Jul 2025 10:19:47 -0700 Subject: [PATCH 2/3] [AST/Sema] SE-0487: Expand `@nonexhaustive` attribute to support `warn` argument The spelling `@nonexhaustive(warn)` replaces `@preEnumExtensibility` attriubte. --- include/swift/AST/Attr.h | 39 +++++++++++++++++++ include/swift/AST/AttrKind.h | 6 +++ include/swift/AST/DeclAttr.def | 2 +- include/swift/AST/KnownIdentifiers.def | 1 + include/swift/Parse/IDEInspectionCallbacks.h | 3 +- lib/AST/Attr.cpp | 15 +++++++ lib/IDE/CompletionLookup.cpp | 5 +++ lib/Parse/ParseDecl.cpp | 15 +++++++ lib/Sema/TypeCheckSwitchStmt.cpp | 22 +++++++---- lib/Serialization/Deserialization.cpp | 8 ++++ lib/Serialization/ModuleFormat.h | 5 +++ lib/Serialization/Serialization.cpp | 7 ++++ test/IDE/complete_nonexhaustive.swift | 9 +++++ test/ModuleInterface/nonexhaustive_attr.swift | 5 +-- .../ModuleInterface/nonexhaustive_enums.swift | 3 +- test/attr/attr_nonexhaustive.swift | 6 +-- 16 files changed, 133 insertions(+), 18 deletions(-) create mode 100644 test/IDE/complete_nonexhaustive.swift diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 7a1e809f293fe..d6aa76eba9c81 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -136,6 +136,11 @@ enum : unsigned { InheritActorContextModifier::Last_InheritActorContextKind)) }; +enum : unsigned { + NumNonexhaustiveModeBits = countBitsUsed( + static_cast(NonexhaustiveMode::Last_NonexhaustiveMode)) +}; + enum : unsigned { NumDeclAttrKindBits = countBitsUsed(NumDeclAttrKinds - 1) }; enum : unsigned { NumTypeAttrKindBits = countBitsUsed(NumTypeAttrKinds - 1) }; @@ -277,6 +282,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 @@ -3500,6 +3509,36 @@ 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(); + } +}; + + /// The kind of unary operator, if any. enum class UnaryOperatorKind : uint8_t { None, Prefix, Postfix }; diff --git a/include/swift/AST/AttrKind.h b/include/swift/AST/AttrKind.h index f18642887279c..aa9196aea1cf8 100644 --- a/include/swift/AST/AttrKind.h +++ b/include/swift/AST/AttrKind.h @@ -135,6 +135,12 @@ enum class ENUM_EXTENSIBILITY_ATTR(closed) Last_InheritActorContextKind = Always }; +enum class ENUM_EXTENSIBILITY_ATTR(closed) NonexhaustiveMode : uint8_t { + Error SWIFT_NAME("error") = 0, + Warning SWIFT_NAME("warning") = 1, + Last_NonexhaustiveMode = Warning +}; + enum class ENUM_EXTENSIBILITY_ATTR(closed) DeclAttrKind : unsigned { #define DECL_ATTR(_, CLASS, ...) CLASS, #define LAST_DECL_ATTR(CLASS) Last_DeclAttr = CLASS, diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index f0a0949c945b4..1617e12f731a7 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -877,7 +877,7 @@ SIMPLE_DECL_ATTR(constInitialized, ConstInitialized, 168) DECL_ATTR_FEATURE_REQUIREMENT(ConstInitialized, CompileTimeValues) -SIMPLE_DECL_ATTR(nonexhaustive, Nonexhaustive, +DECL_ATTR(nonexhaustive, Nonexhaustive, OnEnum, ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | ForbiddenInABIAttr, 169) 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/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/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 744b7c3b65a8e..e08808d49d603 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1732,6 +1732,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"); @@ -1967,6 +1980,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/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index 21c4de6ac465d..ed8d108fa72db 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -3278,6 +3278,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 fcb11e0e89d29..78de6ddde3484 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4033,6 +4033,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/TypeCheckSwitchStmt.cpp b/lib/Sema/TypeCheckSwitchStmt.cpp index 41ebaf200de62..2dcc2b59297df 100644 --- a/lib/Sema/TypeCheckSwitchStmt.cpp +++ b/lib/Sema/TypeCheckSwitchStmt.cpp @@ -1167,14 +1167,20 @@ namespace { DE.diagnose(startLoc, diag::non_exhaustive_switch_unknown_only, subjectType, shouldIncludeFutureVersionComment); - // Presence of `@preEnumExtensibility` pushed the warning farther - // into the future. - if (theEnum && - theEnum->getAttrs().hasAttribute()) { - diag.warnUntilFutureSwiftVersion(); - } else { - diag.warnUntilSwiftVersion(6); - } + 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; } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 7a1d9a5bb074e..ba5279eeac455 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -6676,6 +6676,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/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 52a69ffa34f92..3837f86d19767 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -2591,6 +2591,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 59fd3dfef2851..8519a2eb40143 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3548,6 +3548,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/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 index f918662375631..c4151e36d5177 100644 --- a/test/ModuleInterface/nonexhaustive_attr.swift +++ b/test/ModuleInterface/nonexhaustive_attr.swift @@ -18,12 +18,11 @@ public enum E { } // CHECK: #if compiler(>=5.3) && $NonexhaustiveAttribute -// CHECK-NEXT: @preEnumExtensibility @nonexhaustive public enum F { +// CHECK-NEXT: @nonexhaustive(warn) public enum F { // CHECK: #else // CHECK-NEXT: public enum F { // CHECK: #endif -@preEnumExtensibility -@nonexhaustive +@nonexhaustive(warn) public enum F { case a } diff --git a/test/ModuleInterface/nonexhaustive_enums.swift b/test/ModuleInterface/nonexhaustive_enums.swift index bae996ba72a87..7f28cd5202187 100644 --- a/test/ModuleInterface/nonexhaustive_enums.swift +++ b/test/ModuleInterface/nonexhaustive_enums.swift @@ -43,8 +43,7 @@ public enum E { case a } -@preEnumExtensibility -@nonexhaustive +@nonexhaustive(warn) public enum PE { case a } diff --git a/test/attr/attr_nonexhaustive.swift b/test/attr/attr_nonexhaustive.swift index 1ba9761dafb16..48dfa19270efc 100644 --- a/test/attr/attr_nonexhaustive.swift +++ b/test/attr/attr_nonexhaustive.swift @@ -39,11 +39,11 @@ struct Test { } } -@preEnumExtensibility -@nonexhaustive +@nonexhaustive(warn) public enum PE { } -@preEnumExtensibility // expected-error {{@preEnumExtensibility can only be used together with '@nonexhaustive' attribute}} +@nonexhaustive // expected-note {{attribute already specified here}} +@nonexhaustive(warn) // expected-error {{duplicate attribute}} public enum WrongPreE { } From 0444d297b6fddcd5cd3adbc7ccd34e120ef3ebf8 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 4 Jul 2025 11:06:44 -0700 Subject: [PATCH 3/3] [AST/Sema] SE-0487: Remove `@preEnumExtensibility` attribute --- include/swift/AST/DeclAttr.def | 6 +----- include/swift/AST/DiagnosticsSema.def | 3 --- lib/AST/ASTDumper.cpp | 1 - lib/AST/ASTPrinter.cpp | 3 +-- lib/ASTGen/Sources/ASTGen/DeclAttrs.swift | 1 - lib/Sema/TypeCheckAttr.cpp | 8 -------- lib/Sema/TypeCheckDeclOverride.cpp | 1 - 7 files changed, 2 insertions(+), 21 deletions(-) diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index 1617e12f731a7..51ed6e7808f39 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -888,11 +888,7 @@ SIMPLE_DECL_ATTR(concurrent, Concurrent, ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 170) -SIMPLE_DECL_ATTR(preEnumExtensibility, PreEnumExtensibility, - OnEnum, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, - 171) -DECL_ATTR_FEATURE_REQUIREMENT(PreEnumExtensibility, NonexhaustiveAttribute) +// Unused '171': Used to be `@preEnumExtensibility` DECL_ATTR(specialized, Specialized, OnConstructor | OnFunc | OnAccessor, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ea16193d2a78a..e968c6cd4aece 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8811,9 +8811,6 @@ ERROR(nonexhaustive_attr_on_internal_type,none, "%select{private|fileprivate|internal|%error|%error|%error}1", (DeclName, AccessLevel)) -ERROR(pre_enum_extensibility_without_nonexhaustive,none, - "%0 can only be used together with '@nonexhaustive' attribute", (DeclAttribute)) - //===----------------------------------------------------------------------===// // MARK: `using` declaration //===----------------------------------------------------------------------===// diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 2aaf1af0f07f1..a320104d9fc09 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -5049,7 +5049,6 @@ class PrintAttribute : public AttributeVisitor, TRIVIAL_ATTR_PRINTER(WeakLinked, weak_linked) TRIVIAL_ATTR_PRINTER(Nonexhaustive, nonexhaustive) TRIVIAL_ATTR_PRINTER(Concurrent, concurrent) - TRIVIAL_ATTR_PRINTER(PreEnumExtensibility, preEnumExtensibility) #undef TRIVIAL_ATTR_PRINTER diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index c191a4fbfad1e..bcbac3a89b5a4 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3327,8 +3327,7 @@ suppressingFeatureAddressableTypes(PrintOptions &options, static void suppressingFeatureNonexhaustiveAttribute(PrintOptions &options, llvm::function_ref action) { - ExcludeAttrRAII scope1(options.ExcludeAttrList, DeclAttrKind::Nonexhaustive); - ExcludeAttrRAII scope2(options.ExcludeAttrList, DeclAttrKind::PreEnumExtensibility); + ExcludeAttrRAII scope(options.ExcludeAttrList, DeclAttrKind::Nonexhaustive); action(); } diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 0d4a7b3481c19..4ff2f698add29 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -240,7 +240,6 @@ extension ASTGenVisitor { .EagerMove, .Exported, .Nonexhaustive, - .PreEnumExtensibility, .DiscardableResult, .DisfavoredOverload, .DynamicMemberLookup, diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 09f98d34056e4..b933b22380765 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -248,14 +248,6 @@ class AttributeChecker : public AttributeVisitor { } } - void visitPreEnumExtensibilityAttr(PreEnumExtensibilityAttr *attr) { - if (!D->getAttrs().hasAttribute()) { - diagnoseAndRemoveAttr( - attr, diag::pre_enum_extensibility_without_nonexhaustive, attr); - return; - } - } - void visitConcurrentAttr(ConcurrentAttr *attr) { checkExecutionBehaviorAttribute(attr); diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index b998e24f58b8e..dc0a7a10f3d01 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1615,7 +1615,6 @@ namespace { UNINTERESTING_ATTR(Optimize) UNINTERESTING_ATTR(Exclusivity) UNINTERESTING_ATTR(Nonexhaustive) - UNINTERESTING_ATTR(PreEnumExtensibility) UNINTERESTING_ATTR(NoLocks) UNINTERESTING_ATTR(NoAllocation) UNINTERESTING_ATTR(NoRuntime)