Skip to content

Commit 2364a7c

Browse files
authored
Merge pull request #33970 from xymus/spi-inline
[Sema] Accept the use of SPI in SPI inlinable code
2 parents f898747 + 357bf8f commit 2364a7c

File tree

6 files changed

+87
-22
lines changed

6 files changed

+87
-22
lines changed

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,8 @@ class InheritedProtocolCollector {
481481
return false;
482482
assert(nominal->isGenericContext());
483483

484+
if (printOptions.PrintSPIs)
485+
out << "@_spi(" << DummyProtocolName << ")\n";
484486
out << "@available(*, unavailable)\nextension ";
485487
nominal->getDeclaredType().print(out, printOptions);
486488
out << " : ";

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
7171
if (D->getDeclContext()->isLocalContext())
7272
return false;
7373

74-
// Public non-SPI declarations are OK.
74+
// Public declarations or SPI used from SPI are OK.
7575
if (D->getFormalAccessScope(/*useDC=*/nullptr,
7676
Kind.allowUsableFromInline).isPublic() &&
77-
!D->isSPI())
77+
!(D->isSPI() && !DC->getInnermostDeclarationDeclContext()->isSPI()))
7878
return false;
7979

8080
auto &Context = DC->getASTContext();
@@ -149,14 +149,15 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
149149

150150
static bool diagnoseDeclExportability(SourceLoc loc, const ValueDecl *D,
151151
const SourceFile &userSF,
152+
const DeclContext *userDC,
152153
FragileFunctionKind fragileKind) {
153154
assert(fragileKind.kind != FragileFunctionKind::None);
154155

155156
auto definingModule = D->getModuleContext();
156157

157-
bool isImplementationOnly =
158-
userSF.isImportedImplementationOnly(definingModule);
159-
if (!isImplementationOnly && !userSF.isImportedAsSPI(D))
158+
auto originKind = getDisallowedOriginKind(
159+
D, userSF, userDC->getInnermostDeclarationDeclContext());
160+
if (originKind == DisallowedOriginKind::None)
160161
return false;
161162

162163
// TODO: different diagnostics
@@ -165,7 +166,7 @@ static bool diagnoseDeclExportability(SourceLoc loc, const ValueDecl *D,
165166
D->getDescriptiveKind(), D->getName(),
166167
static_cast<unsigned>(fragileKind.kind),
167168
definingModule->getName(),
168-
static_cast<unsigned>(!isImplementationOnly));
169+
static_cast<unsigned>(originKind));
169170
return true;
170171
}
171172

@@ -234,7 +235,7 @@ TypeChecker::diagnoseDeclRefExportability(SourceLoc loc,
234235
return false;
235236

236237
const ValueDecl *D = declRef.getDecl();
237-
if (diagnoseDeclExportability(loc, D, *userSF, fragileKind))
238+
if (diagnoseDeclExportability(loc, D, *userSF, DC, fragileKind))
238239
return true;
239240
if (diagnoseGenericArgumentsExportability(loc, declRef.getSubstitutions(),
240241
*userSF, DC)) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -893,8 +893,10 @@ void AttributeChecker::visitSPIAccessControlAttr(SPIAccessControlAttr *attr) {
893893
if (auto VD = dyn_cast<ValueDecl>(D)) {
894894
// VD must be public or open to use an @_spi attribute.
895895
auto declAccess = VD->getFormalAccess();
896+
auto DC = VD->getDeclContext()->getAsDecl();
896897
if (declAccess < AccessLevel::Public &&
897-
!VD->getAttrs().hasAttribute<UsableFromInlineAttr>()) {
898+
!VD->getAttrs().hasAttribute<UsableFromInlineAttr>() &&
899+
!(DC && DC->isSPI())) {
898900
diagnoseAndRemoveAttr(attr,
899901
diag::spi_attribute_on_non_public,
900902
declAccess,
@@ -904,7 +906,9 @@ void AttributeChecker::visitSPIAccessControlAttr(SPIAccessControlAttr *attr) {
904906
// Forbid stored properties marked SPI in frozen types.
905907
if (auto property = dyn_cast<AbstractStorageDecl>(VD))
906908
if (auto DC = dyn_cast<NominalTypeDecl>(D->getDeclContext()))
907-
if (property->hasStorage() && !DC->isFormallyResilient())
909+
if (property->hasStorage() &&
910+
!DC->isFormallyResilient() &&
911+
!DC->isSPI())
908912
diagnoseAndRemoveAttr(attr,
909913
diag::spi_attribute_on_frozen_stored_properties,
910914
VD->getName());

test/SPI/local_spi_decls.swift

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@
77
// RUN: %target-typecheck-verify-swift -I %t -verify-ignore-unknown -swift-version 5
88

99
// SPI declarations
10-
@_spi(MySPI) public func spiFunc() {} // expected-note {{global function 'spiFunc()' is not '@usableFromInline' or public}}
10+
@_spi(MySPI) public func spiFunc() {} // expected-note 2 {{global function 'spiFunc()' is not '@usableFromInline' or public}}
1111
@_spi(+) public func invalidSPIName() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}}
1212
@_spi(🤔) public func emojiNamedSPI() {}
1313
@_spi() public func emptyParensSPI() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}}
1414
@_spi(set) public func keywordSPI() {} // expected-error {{expected an SPI identifier as subject of the '@_spi' attribute}}
1515

16-
@_spi(S) public class SPIClass {} // expected-note 3 {{type declared here}}
17-
// expected-note @-1 2 {{class 'SPIClass' is not '@usableFromInline' or public}}
16+
@_spi(S) public class SPIClass { // expected-note 5 {{type declared here}}
17+
// expected-note @-1 3 {{class 'SPIClass' is not '@usableFromInline' or public}}
18+
// expected-note @-2 {{class 'SPIClass' is not public}}
19+
public init() {}
20+
}
1821
class InternalClass {} // expected-note 2 {{type declared here}}
1922
private class PrivateClass {} // expected-note 2 {{type declared here}}
2023

@@ -31,13 +34,25 @@ func inlinable() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' a
3134
_ = SPIClass() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}}
3235
}
3336

34-
@_spi(S) public struct SPIStruct {} // expected-note 2 {{struct 'SPIStruct' is not '@usableFromInline' or public}}
37+
@_spi(S) public struct SPIStruct { // expected-note 2 {{struct 'SPIStruct' is not '@usableFromInline' or public}}
38+
public init() {}
39+
}
3540

3641
@frozen public struct FrozenStruct {
3742
@_spi(S) public var spiInFrozen = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}}
3843
// expected-error @-1 {{stored property 'spiInFrozen' cannot be declared '@_spi' in a '@frozen' struct}}
3944

40-
var asdf = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}}
45+
var spiTypeInFrozen = SPIStruct() // expected-error {{struct 'SPIStruct' is '@_spi' and cannot be referenced from a property initializer in a '@frozen' type}}
46+
private var spiTypeInFrozen1: SPIClass // expected-error {{cannot use class 'SPIClass' here; it is SPI}}
47+
}
48+
49+
@_spi(S)
50+
@frozen public struct SPIFrozenStruct {
51+
var spiTypeInFrozen = SPIStruct()
52+
private var spiTypeInFrozen1: SPIClass
53+
54+
@_spi(S)
55+
private var privateSPIInFrozenSPI = SPIStruct()
4156
}
4257

4358
private protocol PrivateProtocol {} // expected-note {{type declared here}}
@@ -81,3 +96,22 @@ public struct NestedParent {
8196
public struct Nested { }
8297
let nested: Nested
8398
}
99+
100+
public func publicFuncWithDefaultValue(_ p: SPIClass = SPIClass()) {} // expected-error {{cannot use class 'SPIClass' here; it is SPI}}
101+
// expected-error @-1 {{class 'SPIClass' is '@_spi' and cannot be referenced from a default argument value}}
102+
103+
@_spi(S)
104+
public func spiFuncWithDefaultValue(_ p: SPIClass = SPIClass()) {}
105+
106+
@inlinable
107+
public func inlinablePublic() {
108+
spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}}
109+
let _ = SPIClass() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}}
110+
}
111+
112+
@_spi(S)
113+
@inlinable
114+
public func inlinableSPI() {
115+
spiFunc()
116+
let _ = SPIClass()
117+
}

test/SPI/private_swiftinterface.swift

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,19 @@
1212
// RUN: %target-swift-frontend -emit-module %t/SPIHelper.swiftinterface -emit-module-path %t/SPIHelper-from-public-swiftinterface.swiftmodule -swift-version 5 -module-name SPIHelper -enable-library-evolution
1313

1414
/// Test the textual interfaces generated from this test.
15-
// RUN: %target-swift-frontend -typecheck %s -emit-module-interface-path %t/main.swiftinterface -emit-private-module-interface-path %t/main.private.swiftinterface -enable-library-evolution -swift-version 5 -I %t
16-
// RUN: %FileCheck -check-prefix=CHECK-PUBLIC %s < %t/main.swiftinterface
17-
// RUN: %FileCheck -check-prefix=CHECK-PRIVATE %s < %t/main.private.swiftinterface
15+
// RUN: %target-swift-frontend -typecheck %s -emit-module-interface-path %t/Main.swiftinterface -emit-private-module-interface-path %t/Main.private.swiftinterface -enable-library-evolution -swift-version 5 -I %t -module-name Main
16+
// RUN: %FileCheck -check-prefix=CHECK-PUBLIC %s < %t/Main.swiftinterface
17+
// RUN: %FileCheck -check-prefix=CHECK-PRIVATE %s < %t/Main.private.swiftinterface
18+
// RUN: %target-swift-frontend -typecheck-module-from-interface -I %t %t/Main.swiftinterface
19+
// RUN: %target-swift-frontend -typecheck-module-from-interface -I %t %t/Main.private.swiftinterface -module-name Main
1820

1921
/// Serialize and deserialize this module, then print.
20-
// RUN: %target-swift-frontend -emit-module %s -emit-module-path %t/merged-partial.swiftmodule -swift-version 5 -I %t -module-name merged -enable-library-evolution
21-
// RUN: %target-swift-frontend -merge-modules %t/merged-partial.swiftmodule -module-name merged -emit-module -emit-module-path %t/merged.swiftmodule -I %t -emit-module-interface-path %t/merged.swiftinterface -emit-private-module-interface-path %t/merged.private.swiftinterface -enable-library-evolution -swift-version 5 -I %t
22-
// RUN: %FileCheck -check-prefix=CHECK-PUBLIC %s < %t/merged.swiftinterface
23-
// RUN: %FileCheck -check-prefix=CHECK-PRIVATE %s < %t/merged.private.swiftinterface
22+
// RUN: %target-swift-frontend -emit-module %s -emit-module-path %t/Merged-partial.swiftmodule -swift-version 5 -I %t -module-name Merged -enable-library-evolution
23+
// RUN: %target-swift-frontend -merge-modules %t/Merged-partial.swiftmodule -module-name Merged -emit-module -emit-module-path %t/Merged.swiftmodule -I %t -emit-module-interface-path %t/Merged.swiftinterface -emit-private-module-interface-path %t/Merged.private.swiftinterface -enable-library-evolution -swift-version 5 -I %t
24+
// RUN: %FileCheck -check-prefix=CHECK-PUBLIC %s < %t/Merged.swiftinterface
25+
// RUN: %FileCheck -check-prefix=CHECK-PRIVATE %s < %t/Merged.private.swiftinterface
26+
// RUN: %target-swift-frontend -typecheck-module-from-interface -I %t %t/Merged.swiftinterface
27+
// RUN: %target-swift-frontend -typecheck-module-from-interface -I %t %t/Merged.private.swiftinterface -module-name Merged
2428

2529
@_spi(HelperSPI) @_spi(OtherSPI) @_spi(OtherSPI) import SPIHelper
2630
// CHECK-PUBLIC: import SPIHelper
@@ -162,6 +166,18 @@ extension IOIPublicStruct : LocalPublicProto {}
162166
// CHECK-PRIVATE-NOT: IOIPublicStruct
163167
// CHECK-PUBLIC-NOT: IOIPublicStruct
164168

169+
@_spi(S)
170+
@frozen public struct SPIFrozenStruct {
171+
// CHECK-PRIVATE: struct SPIFrozenStruct
172+
// CHECK-PUBLIC-NOT: SPIFrozenStruct
173+
174+
var spiTypeInFrozen = SPIStruct()
175+
// CHECK-PRIVATE: @_spi(S) internal var spiTypeInFrozen
176+
177+
private var spiTypeInFrozen1: SPIClass
178+
// CHECK-PRIVATE: @_spi(S) private var spiTypeInFrozen1
179+
}
180+
165181
// The dummy conformance should be only in the private swiftinterface for
166182
// SPI extensions.
167183
@_spi(LocalSPI)

test/SPI/spi_client.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,16 @@ public func publicUseOfSPI(param: SPIClass) -> SPIClass {} // expected-error 2{{
5353
public func publicUseOfSPI2() -> [SPIClass] {} // expected-error {{cannot use class 'SPIClass' here; it is an SPI imported from 'SPIHelper'}}
5454

5555
@inlinable
56-
func inlinable() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}}
56+
public func inlinable() -> SPIClass { // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}}
5757
spiFunc() // expected-error {{global function 'spiFunc()' is '@_spi' and cannot be referenced from an '@inlinable' function}}
5858
_ = SPIClass() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}}
5959
_ = [SPIClass]() // expected-error {{class 'SPIClass' is '@_spi' and cannot be referenced from an '@inlinable' function}}
6060
}
61+
62+
@_spi(S)
63+
@inlinable
64+
public func inlinable() -> SPIClass {
65+
spiFunc()
66+
_ = SPIClass()
67+
_ = [SPIClass]()
68+
}

0 commit comments

Comments
 (0)