From 80149d627a382105b16a2b2a409be2dbea4dbee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 12 Dec 2025 10:38:45 -0800 Subject: [PATCH 1/5] Sema: Always allow on `@_implementationOnly` on types --- include/swift/AST/DiagnosticsSema.def | 3 - lib/Sema/TypeCheckAttr.cpp | 6 -- test/Sema/hidden-memory-layout.swift | 85 +++++-------------- ...ation_only_on_types_feature_required.swift | 4 - 4 files changed, 22 insertions(+), 76 deletions(-) delete mode 100644 test/attr/attr_implementation_only_on_types_feature_required.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 5084949ac2c5d..ee5026e2bcd93 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3958,9 +3958,6 @@ ERROR(implementation_only_override_import_without_attr,none, "override of %kindonly0 imported as implementation-only must be declared " "'@_implementationOnly'", (const ValueDecl *)) -ERROR(implementation_only_on_types_feature,none, - "'@_implementationOnly' on a type requires " - "'-enable-experimental-feature CheckImplementationOnly'", ()) ERROR(import_attr_conflict,none, "%0 inconsistently imported with %1", diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 36cf611dae2f7..11061bbc75646 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -5058,12 +5058,6 @@ AttributeChecker::visitImplementationOnlyAttr(ImplementationOnlyAttr *attr) { // @_implementationOnly on types only applies to non-public types. if (isa(D)) { - if (!Ctx.LangOpts.hasFeature(Feature::CheckImplementationOnly) && - !Ctx.LangOpts.hasFeature(Feature::CheckImplementationOnlyStrict)) { - diagnoseAndRemoveAttr(attr, diag::implementation_only_on_types_feature); - return; - } - auto access = VD->getFormalAccessScope(/*useDC=*/nullptr, /*treatUsableFromInlineAsPublic=*/true); diff --git a/test/Sema/hidden-memory-layout.swift b/test/Sema/hidden-memory-layout.swift index c70b5cec121c4..5f52b8edc5bab 100644 --- a/test/Sema/hidden-memory-layout.swift +++ b/test/Sema/hidden-memory-layout.swift @@ -10,15 +10,15 @@ // RUN: %S/Inputs/implementation-only-imports/directs.swift \ // RUN: -swift-version 5 -/// Old diags +/// Default diags // RUN: %target-swift-frontend -emit-module -verify -verify-ignore-unrelated %s -I %t \ // RUN: -swift-version 5 \ // RUN: -verify-additional-prefix not-opt-in- -/// New diags +/// Opt-in diags // RUN: %target-swift-frontend -emit-module -verify -verify-ignore-unrelated %s -I %t \ // RUN: -swift-version 5 \ -// RUN: -verify-additional-prefix opt-in- -DUseImplementationOnly \ +// RUN: -verify-additional-prefix opt-in- \ // RUN: -enable-experimental-feature CheckImplementationOnly /// Embedded @@ -34,17 +34,17 @@ // RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded -/// Old diags +/// Default diags // RUN: %target-swift-frontend -emit-module -verify -verify-ignore-unrelated %s -I %t \ // RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded \ // RUN: -verify-additional-prefix not-opt-in- -/// New diags +/// Opt-in diags // RUN: %target-swift-frontend -emit-module -verify -verify-ignore-unrelated %s -I %t \ // RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded \ -// RUN: -verify-additional-prefix opt-in- -DUseImplementationOnly \ +// RUN: -verify-additional-prefix opt-in- \ // RUN: -verify-additional-prefix embedded-opt-in- \ // RUN: -enable-experimental-feature CheckImplementationOnly @@ -52,7 +52,7 @@ // RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unrelated %s -I %t \ // RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded \ -// RUN: -verify-additional-prefix opt-in- -DUseImplementationOnly \ +// RUN: -verify-additional-prefix opt-in- \ // RUN: -verify-additional-prefix embedded-opt-in- \ // RUN: -enable-experimental-feature CheckImplementationOnlyStrict @@ -95,37 +95,21 @@ private class ExposedClassPrivate { init() { fatalError() } // expected-note {{initializer 'init()' is not '@usableFromInline' or public}} } -#if UseImplementationOnly @_implementationOnly private class HiddenClass { -// expected-opt-in-note @-1 2 {{class 'HiddenClass' is not '@usableFromInline' or public}} -// expected-opt-in-note @-2 1 {{initializer 'init()' is not '@usableFromInline' or public}} -// expected-opt-in-note @-3 7 {{class declared here}} -// expected-opt-in-note @-4 3 {{type declared here}} -} -#else -private class HiddenClass { -// expected-not-opt-in-note @-1 2 {{class 'HiddenClass' is not '@usableFromInline' or public}} -// expected-not-opt-in-note @-2 1 {{initializer 'init()' is not '@usableFromInline' or public}} -// expected-not-opt-in-note @-3 3 {{type declared here}} +// expected-note @-1 2 {{class 'HiddenClass' is not '@usableFromInline' or public}} +// expected-note @-2 1 {{initializer 'init()' is not '@usableFromInline' or public}} +// expected-note @-3 3 {{type declared here}} +// expected-opt-in-note @-4 7 {{class declared here}} } -#endif -#if UseImplementationOnly @_implementationOnly private struct HiddenLayout { -// expected-opt-in-note @-1 2 {{struct 'HiddenLayout' is not '@usableFromInline' or public}} -// expected-opt-in-note @-2 1 {{initializer 'init()' is not '@usableFromInline' or public}} +// expected-note @-1 2 {{struct 'HiddenLayout' is not '@usableFromInline' or public}} +// expected-note @-2 1 {{initializer 'init()' is not '@usableFromInline' or public}} // expected-opt-in-note @-3 9 {{struct declared here}} -// expected-opt-in-note @-4 4 {{type declared here}} -} -#else -private struct HiddenLayout { -// expected-not-opt-in-note @-1 2 {{struct 'HiddenLayout' is not '@usableFromInline' or public}} -// expected-not-opt-in-note @-2 1 {{initializer 'init()' is not '@usableFromInline' or public}} -// expected-not-opt-in-note @-3 4 {{type declared here}} +// expected-note @-4 4 {{type declared here}} } -#endif public enum ExposedEnumPublic { case A @@ -140,25 +124,15 @@ private enum ExposedEnumPrivate { case B } -#if UseImplementationOnly @_implementationOnly private enum HiddenEnum { // expected-opt-in-note @-1 6 {{enum declared here}} -// expected-opt-in-note @-2 2 {{enum 'HiddenEnum' is not '@usableFromInline' or public}} -// expected-opt-in-note @-3 2 {{type declared here}} - case A -// expected-opt-in-note @-1 {{enum case 'A' is not '@usableFromInline' or public}} - case B -} -#else -private enum HiddenEnum { -// expected-not-opt-in-note @-1 2 {{enum 'HiddenEnum' is not '@usableFromInline' or public}} -// expected-not-opt-in-note @-2 2 {{type declared here}} +// expected-note @-2 2 {{enum 'HiddenEnum' is not '@usableFromInline' or public}} +// expected-note @-3 2 {{type declared here}} case A -// expected-not-opt-in-note @-1 {{enum case 'A' is not '@usableFromInline' or public}} +// expected-note @-1 {{enum case 'A' is not '@usableFromInline' or public}} case B } -#endif public protocol ExposedProtocolPublic { } @@ -173,19 +147,12 @@ private protocol ExposedProtocolPrivate { // expected-note @-2 4 {{type declared here}} } -#if UseImplementationOnly @_implementationOnly private protocol HiddenProtocol { -// expected-opt-in-note @-1 {{protocol 'HiddenProtocol' is not '@usableFromInline' or public}} +// expected-note @-1 {{protocol 'HiddenProtocol' is not '@usableFromInline' or public}} // expected-opt-in-note @-2 9 {{protocol declared here}} -// expected-opt-in-note @-3 4 {{type declared here}} -} -#else -private protocol HiddenProtocol { -// expected-not-opt-in-note @-1 1 {{protocol 'HiddenProtocol' is not '@usableFromInline' or public}} -// expected-not-opt-in-note @-2 4 {{type declared here}} +// expected-note @-3 4 {{type declared here}} } -#endif @_spi(S) public struct SPIStruct {} // expected-note @-1 {{struct declared here}} @@ -491,7 +458,6 @@ private struct ExposedLayoutPrivateUser: ProtocolFromDirect { @_spi(S) public var s: SPIStruct } -#if UseImplementationOnly @_implementationOnly private struct HiddenLayoutUser { public var publicField: StructFromDirect @@ -522,11 +488,9 @@ private struct HiddenLayoutUser { private func privateFuncClass(h: HiddenClass) {} } -@_implementationOnly // expected-opt-in-error {{'@_implementationOnly' may not be used on public declarations}} +@_implementationOnly // expected-error {{'@_implementationOnly' may not be used on public declarations}} public struct PublicHiddenStruct {} -#endif - /// Enums use sites public enum PublicEnumUser: ProtocolFromDirect { @@ -600,7 +564,6 @@ internal enum InternalEnumWithRawType : RawTypeFromDirect { // expected-opt-in-e case a } -#if UseImplementationOnly @_implementationOnly private enum PrivateHiddenEnumUser: ProtocolFromDirect { case a(StructFromDirect) @@ -621,7 +584,7 @@ private enum PrivateHiddenEnumUser: ProtocolFromDirect { case i(HiddenProtocol) } -@_implementationOnly // expected-opt-in-error {{'@_implementationOnly' may not be used on public declarations}} +@_implementationOnly // expected-error {{'@_implementationOnly' may not be used on public declarations}} public enum PublicHiddenEnum {} @_implementationOnly @@ -630,8 +593,6 @@ internal enum InternalEnumWithRawTypeIO : RawTypeFromDirect { case a } -#endif - /// Classes use sites public class PublicClassUser: ProtocolFromDirect { @@ -782,7 +743,6 @@ private class PrivateClassUser: ProtocolFromDirect { private func privateFunc(h: HiddenLayout) {} // expected-embedded-opt-in-error {{struct 'HiddenLayout' cannot be used in an embedded function not marked '@export(interface)' because 'HiddenLayout' is marked '@_implementationOnly'}} } -#if UseImplementationOnly @_implementationOnly internal class HiddenClassUser: ProtocolFromDirect { public init() { fatalError() } @@ -810,9 +770,8 @@ internal class HiddenClassUser: ProtocolFromDirect { private var j: HiddenProtocol } -@_implementationOnly // expected-opt-in-error {{'@_implementationOnly' may not be used on public declarations}} +@_implementationOnly // expected-error {{'@_implementationOnly' may not be used on public declarations}} public enum PublicHiddenClass {} -#endif /// Protocol use sites diff --git a/test/attr/attr_implementation_only_on_types_feature_required.swift b/test/attr/attr_implementation_only_on_types_feature_required.swift deleted file mode 100644 index 8383db485cad8..0000000000000 --- a/test/attr/attr_implementation_only_on_types_feature_required.swift +++ /dev/null @@ -1,4 +0,0 @@ -// RUN: %target-typecheck-verify-swift %s - -@_implementationOnly struct MyStruct {} -// expected-error@-1{{'@_implementationOnly' on a type requires '-enable-experimental-feature CheckImplementationOnly'}} From 8b55b25b349d61e4d1269ec937e1e1bbbd1f440e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 12 Dec 2025 14:54:40 -0800 Subject: [PATCH 2/5] Sema: Add diagnostic for implicitly public inheritance --- include/swift/AST/DiagnosticsSema.def | 1 + lib/Sema/ResilienceDiagnostics.cpp | 1 + lib/Sema/TypeCheckAccess.cpp | 12 ++++++++++-- lib/Sema/TypeCheckAvailability.h | 1 + test/Sema/hidden-memory-layout.swift | 14 +++++++------- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ee5026e2bcd93..4c86d0c6aba86 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3855,6 +3855,7 @@ NOTE(construct_raw_representable_from_unwrapped_value,none, "in an extension with public or '@usableFromInline' members|" \ "in an extension with conditional conformances|" \ "in a public or '@usableFromInline' conformance|" \ + "in a conformance on a type not marked '@_implementationOnly'|" \ "in an '@available' attribute here|" \ "in a property declaration marked public or in a '@frozen' or '@usableFromInline' context|" \ "in a property declaration member of a type not marked '@_implementationOnly'}" diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index eceb7a9ed15b3..4cb45997a67ae 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -248,6 +248,7 @@ static bool shouldDiagnoseDeclAccess(const ValueDecl *D, case ExportabilityReason::ExtensionWithConditionalConformances: return true; case ExportabilityReason::Inheritance: + case ExportabilityReason::ImplicitlyPublicInheritance: return isa(D); case ExportabilityReason::AvailableAttribute: // If the context is an extension and that extension has an explicit diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index ddda3acd75eb0..8bf343bf54be5 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -2493,9 +2493,13 @@ class DeclAvailabilityChecker : public DeclVisitor { flags |= DeclAvailabilityFlag:: AllowPotentiallyUnavailableAtOrBelowDeploymentTarget; + ExportabilityReason reason = + Where.getExportedLevel() == ExportedLevel::ImplicitlyExported ? + ExportabilityReason::ImplicitlyPublicInheritance : + ExportabilityReason::Inheritance; for (TypeLoc inherited : nominal->getInherited().getEntries()) { checkType(inherited.getType(), inherited.getTypeRepr(), nominal, - ExportabilityReason::Inheritance, + reason, flags | DeclAvailabilityFlag::DisableUnsafeChecking); } } @@ -2597,9 +2601,13 @@ class DeclAvailabilityChecker : public DeclVisitor { // must be exported. DeclAvailabilityFlags flags = DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; flags |= DeclAvailabilityFlag::DisableUnsafeChecking; + ExportabilityReason inheritanceReason = + Where.getExportedLevel() == ExportedLevel::ImplicitlyExported ? + ExportabilityReason::ImplicitlyPublicInheritance : + ExportabilityReason::Inheritance; for (TypeLoc inherited : ED->getInherited().getEntries()) { checkType(inherited.getType(), inherited.getTypeRepr(), ED, - ExportabilityReason::Inheritance, flags); + inheritanceReason, flags); } auto wasWhere = Where; diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index d4d615336693a..2bf2f487e8864 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -82,6 +82,7 @@ enum class ExportabilityReason : unsigned { ExtensionWithPublicMembers, ExtensionWithConditionalConformances, Inheritance, + ImplicitlyPublicInheritance, AvailableAttribute, PublicVarDecl, ImplicitlyPublicVarDecl, diff --git a/test/Sema/hidden-memory-layout.swift b/test/Sema/hidden-memory-layout.swift index 5f52b8edc5bab..191e67a30761e 100644 --- a/test/Sema/hidden-memory-layout.swift +++ b/test/Sema/hidden-memory-layout.swift @@ -385,7 +385,7 @@ public struct ExposedLayoutPublicUser: ProtocolFromDirect { } internal struct ExposedLayoutInternalUser: ProtocolFromDirect { -// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a public or '@usableFromInline' conformance; 'directs' has been imported as implementation-only}} +// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a conformance on a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} private var privateField: StructFromDirect // expected-opt-in-error @-1 {{cannot use struct 'StructFromDirect' in a property declaration member of a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} @@ -422,7 +422,7 @@ internal struct ExposedLayoutInternalUser: ProtocolFromDirect { } private struct ExposedLayoutPrivateUser: ProtocolFromDirect { -// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a public or '@usableFromInline' conformance; 'directs' has been imported as implementation-only}} +// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a conformance on a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} private var privateField: StructFromDirect // expected-opt-in-error @-1 {{cannot use struct 'StructFromDirect' in a property declaration member of a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} @@ -517,7 +517,7 @@ public enum PublicEnumUser: ProtocolFromDirect { } internal enum InternalEnumUser: ProtocolFromDirect { -// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a public or '@usableFromInline' conformance; 'directs' has been imported as implementation-only}} +// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a conformance on a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} case a(StructFromDirect) // expected-opt-in-error {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}} case e(ExposedLayoutPublic) @@ -540,7 +540,7 @@ internal enum InternalEnumUser: ProtocolFromDirect { } private enum PrivateEnumUser: ProtocolFromDirect { -// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a public or '@usableFromInline' conformance; 'directs' has been imported as implementation-only}} +// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a conformance on a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} case a(StructFromDirect) // expected-opt-in-error {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}} case e(ExposedLayoutPublic) @@ -559,7 +559,7 @@ private enum PrivateEnumUser: ProtocolFromDirect { case i(HiddenProtocol) // expected-opt-in-error {{cannot use protocol 'HiddenProtocol' here; 'HiddenProtocol' is marked '@_implementationOnly'}} } -internal enum InternalEnumWithRawType : RawTypeFromDirect { // expected-opt-in-error {{cannot use struct 'RawTypeFromDirect' in a public or '@usableFromInline' conformance; 'directs' has been imported as implementation-only}} +internal enum InternalEnumWithRawType : RawTypeFromDirect { // expected-opt-in-error {{cannot use struct 'RawTypeFromDirect' in a conformance on a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} typealias RawValue = RawTypeFromDirect case a } @@ -694,7 +694,7 @@ public class FixedClassUser: ProtocolFromDirect { } internal class InternalClassUser: ProtocolFromDirect { -// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a public or '@usableFromInline' conformance; 'directs' has been imported as implementation-only}} +// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a conformance on a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} public init() { fatalError() } @@ -719,7 +719,7 @@ internal class InternalClassUser: ProtocolFromDirect { } private class PrivateClassUser: ProtocolFromDirect { -// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a public or '@usableFromInline' conformance; 'directs' has been imported as implementation-only}} +// expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a conformance on a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} public init() { fatalError() } From cd253e7748c32db373ed9ef3707aa00d1762f8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 12 Dec 2025 15:16:56 -0800 Subject: [PATCH 3/5] Sema: Add diagnostics specific to enum associated values --- include/swift/AST/DiagnosticsSema.def | 4 +++- lib/Sema/ResilienceDiagnostics.cpp | 2 ++ lib/Sema/TypeCheckAccess.cpp | 6 ++++- lib/Sema/TypeCheckAvailability.h | 2 ++ test/SPI/spi_enum_element.swift | 2 +- test/Sema/hidden-memory-layout.swift | 24 +++++++++---------- .../implementation-only-import-in-decls.swift | 4 ++-- test/Sema/spi-in-decls.swift | 4 ++-- 8 files changed, 29 insertions(+), 19 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 4c86d0c6aba86..a9d991842fc68 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3858,7 +3858,9 @@ NOTE(construct_raw_representable_from_unwrapped_value,none, "in a conformance on a type not marked '@_implementationOnly'|" \ "in an '@available' attribute here|" \ "in a property declaration marked public or in a '@frozen' or '@usableFromInline' context|" \ - "in a property declaration member of a type not marked '@_implementationOnly'}" + "in a property declaration member of a type not marked '@_implementationOnly'|" \ + "in an associated value of a public or '@usableFromInline' enum|" \ + "in an associated value of an enum not marked '@_implementationOnly'}" ERROR(decl_from_hidden_module,none, "cannot use %kind0 %" EXPORTABILITY_REASON_SELECT "1; " diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 4cb45997a67ae..56af9513b263d 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -263,6 +263,8 @@ static bool shouldDiagnoseDeclAccess(const ValueDecl *D, case ExportabilityReason::PropertyWrapper: case ExportabilityReason::PublicVarDecl: case ExportabilityReason::ImplicitlyPublicVarDecl: + case ExportabilityReason::AssociatedValue: + case ExportabilityReason::ImplicitlyPublicAssociatedValue: return false; } } diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 8bf343bf54be5..c49585f2aaf42 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -2563,8 +2563,12 @@ class DeclAvailabilityChecker : public DeclVisitor { void visitEnumElementDecl(EnumElementDecl *EED) { if (!EED->hasAssociatedValues()) return; + ExportabilityReason reason = + Where.getExportedLevel() == ExportedLevel::ImplicitlyExported ? + ExportabilityReason::ImplicitlyPublicAssociatedValue : + ExportabilityReason::AssociatedValue; for (auto &P : *EED->getParameterList()) - checkType(P->getInterfaceType(), P->getTypeRepr(), EED); + checkType(P->getInterfaceType(), P->getTypeRepr(), EED, reason); } void visitMacroDecl(MacroDecl *MD) { diff --git a/lib/Sema/TypeCheckAvailability.h b/lib/Sema/TypeCheckAvailability.h index 2bf2f487e8864..95fd4c8367ff4 100644 --- a/lib/Sema/TypeCheckAvailability.h +++ b/lib/Sema/TypeCheckAvailability.h @@ -86,6 +86,8 @@ enum class ExportabilityReason : unsigned { AvailableAttribute, PublicVarDecl, ImplicitlyPublicVarDecl, + AssociatedValue, + ImplicitlyPublicAssociatedValue, }; /// A description of the restrictions on what declarations can be referenced diff --git a/test/SPI/spi_enum_element.swift b/test/SPI/spi_enum_element.swift index 7001a8ff40e5a..51786f31946da 100644 --- a/test/SPI/spi_enum_element.swift +++ b/test/SPI/spi_enum_element.swift @@ -36,7 +36,7 @@ public struct PublicStruct {} public enum PublicEnumWithPayloads { case publicCasePublicPayload(_ s: PublicStruct) - case publicCaseSPIPayload(_ s: SPIStruct) // expected-error {{cannot use struct 'SPIStruct' here; it is SPI}} + case publicCaseSPIPayload(_ s: SPIStruct) // expected-error {{cannot use struct 'SPIStruct' in an associated value of a public or '@usableFromInline' enum; it is SPI}} @_spi(S) case spiCasePublicPayload(_ s: PublicStruct) @_spi(S) case spiCaseSPIPayload(_ s: SPIStruct) } diff --git a/test/Sema/hidden-memory-layout.swift b/test/Sema/hidden-memory-layout.swift index 191e67a30761e..a6cc027354de7 100644 --- a/test/Sema/hidden-memory-layout.swift +++ b/test/Sema/hidden-memory-layout.swift @@ -495,68 +495,68 @@ public struct PublicHiddenStruct {} public enum PublicEnumUser: ProtocolFromDirect { // expected-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a public or '@usableFromInline' conformance; 'directs' has been imported as implementation-only}} - case a(StructFromDirect) // expected-error {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}} + case a(StructFromDirect) // expected-error {{cannot use struct 'StructFromDirect' in an associated value of a public or '@usableFromInline' enum; 'directs' has been imported as implementation-only}} case e(ExposedLayoutPublic) case c(ExposedLayoutInternal) // expected-error {{enum case in a public enum uses an internal type}} case d(ExposedLayoutPrivate) // expected-error {{enum case in a public enum uses a private type}} case b(HiddenLayout) // expected-error {{enum case in a public enum uses a private type}} - // expected-opt-in-error @-1 {{cannot use struct 'HiddenLayout' here; 'HiddenLayout' is marked '@_implementationOnly'}} + // expected-opt-in-error @-1 {{cannot use struct 'HiddenLayout' in an associated value of a public or '@usableFromInline' enum; 'HiddenLayout' is marked '@_implementationOnly'}} case ce(ExposedClassPublic) case cc(ExposedClassInternal) // expected-error {{enum case in a public enum uses an internal type}} case cd(ExposedClassPrivate) // expected-error {{enum case in a public enum uses a private type}} case cb(HiddenClass) // expected-error {{enum case in a public enum uses a private type}} - // expected-opt-in-error @-1 {{cannot use class 'HiddenClass' here; 'HiddenClass' is marked '@_implementationOnly'}} + // expected-opt-in-error @-1 {{cannot use class 'HiddenClass' in an associated value of a public or '@usableFromInline' enum; 'HiddenClass' is marked '@_implementationOnly'}} case f(ExposedProtocolPublic) case g(ExposedProtocolInternal) // expected-error {{enum case in a public enum uses an internal type}} case h(ExposedProtocolPrivate) // expected-error {{enum case in a public enum uses a private type}} - case i(HiddenProtocol) // expected-opt-in-error {{cannot use protocol 'HiddenProtocol' here; 'HiddenProtocol' is marked '@_implementationOnly'}} + case i(HiddenProtocol) // expected-opt-in-error {{cannot use protocol 'HiddenProtocol' in an associated value of a public or '@usableFromInline' enum; 'HiddenProtocol' is marked '@_implementationOnly'}} // expected-error @-1 {{enum case in a public enum uses a private type}} } internal enum InternalEnumUser: ProtocolFromDirect { // expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a conformance on a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} - case a(StructFromDirect) // expected-opt-in-error {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}} + case a(StructFromDirect) // expected-opt-in-error {{cannot use struct 'StructFromDirect' in an associated value of an enum not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} case e(ExposedLayoutPublic) case c(ExposedLayoutInternal) case d(ExposedLayoutPrivate) // expected-error {{enum case in an internal enum uses a private type}} - case b(HiddenLayout) // expected-opt-in-error {{cannot use struct 'HiddenLayout' here; 'HiddenLayout' is marked '@_implementationOnly'}} + case b(HiddenLayout) // expected-opt-in-error {{cannot use struct 'HiddenLayout' in an associated value of an enum not marked '@_implementationOnly'; 'HiddenLayout' is marked '@_implementationOnly'}} // expected-error @-1 {{enum case in an internal enum uses a private type}} case ce(ExposedClassPublic) case cc(ExposedClassInternal) case cd(ExposedClassPrivate) // expected-error {{enum case in an internal enum uses a private type}} - case cb(HiddenClass) // expected-opt-in-error {{cannot use class 'HiddenClass' here; 'HiddenClass' is marked '@_implementationOnly'}} + case cb(HiddenClass) // expected-opt-in-error {{cannot use class 'HiddenClass' in an associated value of an enum not marked '@_implementationOnly'; 'HiddenClass' is marked '@_implementationOnly'}} // expected-error @-1 {{enum case in an internal enum uses a private type}} case f(ExposedProtocolPublic) case g(ExposedProtocolInternal) case h(ExposedProtocolPrivate) // expected-error {{enum case in an internal enum uses a private type}} - case i(HiddenProtocol) // expected-opt-in-error {{cannot use protocol 'HiddenProtocol' here; 'HiddenProtocol' is marked '@_implementationOnly'}} + case i(HiddenProtocol) // expected-opt-in-error {{cannot use protocol 'HiddenProtocol' in an associated value of an enum not marked '@_implementationOnly'; 'HiddenProtocol' is marked '@_implementationOnly'}} // expected-error @-1 {{enum case in an internal enum uses a private type}} } private enum PrivateEnumUser: ProtocolFromDirect { // expected-opt-in-error @-1 {{cannot use protocol 'ProtocolFromDirect' in a conformance on a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} - case a(StructFromDirect) // expected-opt-in-error {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}} + case a(StructFromDirect) // expected-opt-in-error {{cannot use struct 'StructFromDirect' in an associated value of an enum not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} case e(ExposedLayoutPublic) case c(ExposedLayoutInternal) case d(ExposedLayoutPrivate) - case b(HiddenLayout) // expected-opt-in-error {{cannot use struct 'HiddenLayout' here; 'HiddenLayout' is marked '@_implementationOnly'}} + case b(HiddenLayout) // expected-opt-in-error {{cannot use struct 'HiddenLayout' in an associated value of an enum not marked '@_implementationOnly'; 'HiddenLayout' is marked '@_implementationOnly'}} case ce(ExposedClassPublic) case cc(ExposedClassInternal) case cd(ExposedClassPrivate) - case cb(HiddenClass) // expected-opt-in-error {{cannot use class 'HiddenClass' here; 'HiddenClass' is marked '@_implementationOnly'}} + case cb(HiddenClass) // expected-opt-in-error {{cannot use class 'HiddenClass' in an associated value of an enum not marked '@_implementationOnly'; 'HiddenClass' is marked '@_implementationOnly'}} case f(ExposedProtocolPublic) case g(ExposedProtocolInternal) case h(ExposedProtocolPrivate) - case i(HiddenProtocol) // expected-opt-in-error {{cannot use protocol 'HiddenProtocol' here; 'HiddenProtocol' is marked '@_implementationOnly'}} + case i(HiddenProtocol) // expected-opt-in-error {{cannot use protocol 'HiddenProtocol' in an associated value of an enum not marked '@_implementationOnly'; 'HiddenProtocol' is marked '@_implementationOnly'}} } internal enum InternalEnumWithRawType : RawTypeFromDirect { // expected-opt-in-error {{cannot use struct 'RawTypeFromDirect' in a conformance on a type not marked '@_implementationOnly'; 'directs' has been imported as implementation-only}} diff --git a/test/Sema/implementation-only-import-in-decls.swift b/test/Sema/implementation-only-import-in-decls.swift index 2ab9a087f5d18..97f14bff80308 100644 --- a/test/Sema/implementation-only-import-in-decls.swift +++ b/test/Sema/implementation-only-import-in-decls.swift @@ -30,8 +30,8 @@ public struct TestGenericParamsWithOuter { } public enum TestCase { - case x(BadStruct) // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} - case y(Int, BadStruct) // expected-error {{cannot use struct 'BadStruct' here; 'BADLibrary' has been imported as implementation-only}} + case x(BadStruct) // expected-error {{cannot use struct 'BadStruct' in an associated value of a public or '@usableFromInline' enum; 'BADLibrary' has been imported as implementation-only}} + case y(Int, BadStruct) // expected-error {{cannot use struct 'BadStruct' in an associated value of a public or '@usableFromInline' enum; 'BADLibrary' has been imported as implementation-only}} } public func testGenericParams(_: T) {} // expected-error {{cannot use protocol 'BadProto' here; 'BADLibrary' has been imported as implementation-only}} diff --git a/test/Sema/spi-in-decls.swift b/test/Sema/spi-in-decls.swift index fe0fafd24ab7e..a62754cdedbce 100644 --- a/test/Sema/spi-in-decls.swift +++ b/test/Sema/spi-in-decls.swift @@ -61,8 +61,8 @@ public struct TestGenericParams {} // expected-error {{cannot use p public struct TestGenericParamsWhereClause where T: BadProto {} // expected-error {{cannot use protocol 'BadProto' here; it is SPI}} public enum TestCase { - case x(BadStruct) // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} - case y(Int, BadStruct) // expected-error {{cannot use struct 'BadStruct' here; it is SPI}} + case x(BadStruct) // expected-error {{cannot use struct 'BadStruct' in an associated value of a public or '@usableFromInline' enum; it is SPI}} + case y(Int, BadStruct) // expected-error {{cannot use struct 'BadStruct' in an associated value of a public or '@usableFromInline' enum; it is SPI}} } public func testGenericParams(_: T) {} // expected-error {{cannot use protocol 'BadProto' here; it is SPI}} From 77064cd70ea7622803c7f9da955f0c71021ff343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 19 Dec 2025 14:00:53 -0800 Subject: [PATCH 4/5] Sema: Expects any reasons for implicitly exported decls --- lib/Sema/TypeCheckAvailability.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 0f51b88fc3332..013a879d40b8c 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -244,10 +244,9 @@ bool ExportContext::canReferenceOrigin(DisallowedOriginKind originKind) const { // than the library-evolution ones. Implicitly always emit into client code // in embedded mode and implicitly exported layouts in non-library-evolution // mode can reference SPIs and non-public dependencies. - auto reason = getExportabilityReason(); if (getFragileFunctionKind().kind == FragileFunctionKind::EmbeddedAlwaysEmitIntoClient || - (reason && *reason == ExportabilityReason::ImplicitlyPublicVarDecl)) { + getExportedLevel() == ExportedLevel::ImplicitlyExported) { switch (originKind) { case DisallowedOriginKind::None: case DisallowedOriginKind::NonPublicImport: From aa33b4535c64a242b145f8185d3ce9d34fb99f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Tue, 16 Dec 2025 09:48:48 -0800 Subject: [PATCH 5/5] Sema: Fix implicitly exported check for extensions --- lib/AST/Availability.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index dd87e2c17ba5d..ca6a721eb6949 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -1047,20 +1047,22 @@ bool swift::hasConformancesToPublicProtocols(const ExtensionDecl *ED) { ExportedLevel swift::isExported(const ExtensionDecl *ED) { // An extension can only be exported if it extends an exported type. + ExportedLevel exported = ExportedLevel::None; if (auto *NTD = ED->getExtendedNominal()) { - if (isExported(NTD) == ExportedLevel::None) - return ExportedLevel::None; + exported = isExported(NTD); + if (exported == ExportedLevel::None) + return exported; } // If the extension declares a conformance to a public protocol then the // extension is exported. if (hasConformancesToPublicProtocols(ED)) - return ExportedLevel::Exported; + return exported; // If there are any exported members then the extension is exported. - ExportedLevel exported = ExportedLevel::None; + ExportedLevel membersExported = ExportedLevel::None; for (const Decl *D : ED->getMembers()) - exported = std::max(exported, isExported(D)); + membersExported = std::max(membersExported, isExported(D)); - return exported; + return membersExported; }