Skip to content

Commit 7a6e8f0

Browse files
committed
Sema: Narrow fix to allow @_spi_available in extensions
Allow referencing an `@_spi_available` decl in extensions to `@_spi_available` types. This is a narrow fix as it should really be handled as part of the context check but that check is currently too permissive. Fow now let's narrowly allow legal code. And then we should look at revisiting the SPI availability logic, separate it from normal SPI and treat more like availability. Adding a test comparing the behavior of `@_spi` with `@_spi_available` to document the current implementation. rdar://159292698
1 parent a4a95f5 commit 7a6e8f0

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

lib/Sema/TypeCheckAccess.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,7 +2041,21 @@ swift::getDisallowedOriginKind(const Decl *decl,
20412041
return DisallowedOriginKind::None;
20422042
}
20432043
}
2044+
2045+
// Allow SPI available use in an extension to an SPI available type.
2046+
// This is a narrow workaround for rdar://159292698 as this should be
2047+
// handled as part of the `where.isSPI()` above, but that context check
2048+
// is currently too permissive. It allows SPI use in SPI available which
2049+
// can break swiftinterfaces. The SPI availability logic likely need to be
2050+
// separated from normal SPI and treated more like availability.
2051+
auto ext = dyn_cast_or_null<ExtensionDecl>(where.getDeclContext());
2052+
if (ext) {
2053+
auto nominal = ext->getExtendedNominal();
2054+
if (nominal && nominal->isAvailableAsSPI())
2055+
return DisallowedOriginKind::None;
2056+
}
20442057
}
2058+
20452059
// SPI can only be exported in SPI.
20462060
return where.getDeclContext()->getParentModule() == M ?
20472061
DisallowedOriginKind::SPILocal :
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-typecheck-verify-swift \
3+
// RUN: -enable-library-evolution -swift-version 5 \
4+
// RUN: -library-level=api -require-explicit-availability=ignore
5+
6+
// REQUIRES: OS=macosx
7+
8+
@_spi_available(macOS, introduced: 12.0) @available(iOS 12.0, *)
9+
public struct SPIAvailableType { // expected-note {{struct declared here}}
10+
public init() {}
11+
}
12+
13+
@_spi(S)
14+
public struct NormalSPIType {
15+
public init() {}
16+
}
17+
18+
// SPI available in SPI available should be accepted.
19+
20+
@_spi_available(macOS, introduced: 12.0) @available(iOS 12.0, *)
21+
public struct OtherSPIAvailableType {
22+
public func foo(s: SPIAvailableType) {}
23+
}
24+
25+
extension OtherSPIAvailableType {
26+
public func bar(s: SPIAvailableType) {} // expected-error {{cannot use struct 'SPIAvailableType' here; it is SPI}} // FIXME We should allow this.
27+
}
28+
29+
// Normal SPI in normal SPI should be accepted.
30+
31+
@_spi(S)
32+
public struct OtherNormalSPIType {
33+
public func foo(s: SPIAvailableType) {}
34+
}
35+
36+
extension OtherNormalSPIType {
37+
public func bar2(s: SPIAvailableType) {}
38+
}
39+
40+
// Normal SPI in SPI available should be rejected.
41+
42+
@_spi_available(macOS, introduced: 12.0) @available(iOS 12.0, *)
43+
public func SPIAvailableToSPI(s: NormalSPIType) {} // FIXME This should be an error
44+
45+
@inlinable
46+
@_spi_available(macOS, introduced: 12.0) @available(iOS 12.0, *)
47+
public func inlinableSPIAvailable() {
48+
let _: NormalSPIType = NormalSPIType() // FIXME There should be many errors here
49+
}
50+
51+
// SPI available in normal SPI is currently accepted.
52+
53+
@_spi(S)
54+
public func SPIToSPIAvailable(s: NormalSPIType) {}
55+
56+
@inlinable
57+
@_spi(S)
58+
public func inlinableSPI() {
59+
let _: SPIAvailableType = SPIAvailableType()
60+
}

0 commit comments

Comments
 (0)