Skip to content

Commit 1adbe37

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 6f0ab5d commit 1adbe37

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
@@ -2124,7 +2124,21 @@ swift::getDisallowedOriginKind(const Decl *decl,
21242124
return DisallowedOriginKind::None;
21252125
}
21262126
}
2127+
2128+
// Allow SPI available use in an extension to an SPI available type.
2129+
// This is a narrow workaround for rdar://159292698 as this should be
2130+
// handled as part of the `where.isSPI()` above, but that context check
2131+
// is currently too permissive. It allows SPI use in SPI available which
2132+
// can break swiftinterfaces. The SPI availability logic likely need to be
2133+
// separated from normal SPI and treated more like availability.
2134+
auto ext = dyn_cast_or_null<ExtensionDecl>(where.getDeclContext());
2135+
if (ext) {
2136+
auto nominal = ext->getExtendedNominal();
2137+
if (nominal && nominal->isAvailableAsSPI())
2138+
return DisallowedOriginKind::None;
2139+
}
21272140
}
2141+
21282142
// SPI can only be exported in SPI.
21292143
return where.getDeclContext()->getParentModule() == M ?
21302144
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)