diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 492aba619df3d..4a844cf7b2e0f 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -2124,7 +2124,21 @@ swift::getDisallowedOriginKind(const Decl *decl, return DisallowedOriginKind::None; } } + + // Allow SPI available use in an extension to an SPI available type. + // This is a narrow workaround for rdar://159292698 as this should be + // handled as part of the `where.isSPI()` above, but that context check + // is currently too permissive. It allows SPI use in SPI available which + // can break swiftinterfaces. The SPI availability logic likely need to be + // separated from normal SPI and treated more like availability. + auto ext = dyn_cast_or_null(where.getDeclContext()); + if (ext) { + auto nominal = ext->getExtendedNominal(); + if (nominal && nominal->isAvailableAsSPI()) + return DisallowedOriginKind::None; + } } + // SPI can only be exported in SPI. return where.getDeclContext()->getParentModule() == M ? DisallowedOriginKind::SPILocal : diff --git a/test/SPI/spi_and_spi_available.swift b/test/SPI/spi_and_spi_available.swift new file mode 100644 index 0000000000000..72c05e70bdd80 --- /dev/null +++ b/test/SPI/spi_and_spi_available.swift @@ -0,0 +1,60 @@ +// RUN: %empty-directory(%t) +// RUN: %target-typecheck-verify-swift \ +// RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -library-level=api -require-explicit-availability=ignore + +// REQUIRES: OS=macosx + +@_spi_available(macOS, introduced: 12.0) @available(iOS 12.0, *) +public struct SPIAvailableType { // expected-note {{struct declared here}} + public init() {} +} + +@_spi(S) +public struct NormalSPIType { + public init() {} +} + +// SPI available in SPI available should be accepted. + +@_spi_available(macOS, introduced: 12.0) @available(iOS 12.0, *) +public struct OtherSPIAvailableType { + public func foo(s: SPIAvailableType) {} +} + +extension OtherSPIAvailableType { + public func bar(s: SPIAvailableType) {} // expected-error {{cannot use struct 'SPIAvailableType' here; it is SPI}} // FIXME We should allow this. +} + +// Normal SPI in normal SPI should be accepted. + +@_spi(S) +public struct OtherNormalSPIType { + public func foo(s: SPIAvailableType) {} +} + +extension OtherNormalSPIType { + public func bar2(s: SPIAvailableType) {} +} + +// Normal SPI in SPI available should be rejected. + +@_spi_available(macOS, introduced: 12.0) @available(iOS 12.0, *) +public func SPIAvailableToSPI(s: NormalSPIType) {} // FIXME This should be an error + +@inlinable +@_spi_available(macOS, introduced: 12.0) @available(iOS 12.0, *) +public func inlinableSPIAvailable() { + let _: NormalSPIType = NormalSPIType() // FIXME There should be many errors here +} + +// SPI available in normal SPI is currently accepted. + +@_spi(S) +public func SPIToSPIAvailable(s: NormalSPIType) {} + +@inlinable +@_spi(S) +public func inlinableSPI() { + let _: SPIAvailableType = SPIAvailableType() +}