Skip to content

Commit 3545603

Browse files
authored
Merge pull request #77737 from xymus/restrict-spi-operators
Sema: Restrict use of SPI operators to files importing the corresponding SPI group
2 parents 4356019 + a4865be commit 3545603

File tree

8 files changed

+62
-8
lines changed

8 files changed

+62
-8
lines changed

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,9 @@ EXPERIMENTAL_FEATURE(CheckImplementationOnly, true)
561561
/// report references to implementation-only imported modules.
562562
EXPERIMENTAL_FEATURE(CheckImplementationOnlyStrict, false)
563563

564+
/// Check that use sites have the required @_spi import for operators.
565+
EXPERIMENTAL_FEATURE(EnforceSPIOperatorGroup, true)
566+
564567
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
565568
#undef EXPERIMENTAL_FEATURE
566569
#undef UPCOMING_FEATURE

lib/AST/Decl.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5536,9 +5536,19 @@ static bool checkAccess(const DeclContext *useDC, const ValueDecl *VD,
55365536
return srcPkg && usePkg && usePkg->isSamePackageAs(srcPkg);
55375537
}
55385538
case AccessLevel::Public:
5539-
case AccessLevel::Open:
5539+
case AccessLevel::Open: {
5540+
if (VD->getASTContext().LangOpts.hasFeature(
5541+
Feature::EnforceSPIOperatorGroup) &&
5542+
VD->isOperator() && VD->isSPI()) {
5543+
const DeclContext *useFile = useDC->getModuleScopeContext();
5544+
if (useFile->getParentModule() == sourceDC->getParentModule())
5545+
return true;
5546+
auto *useSF = dyn_cast<SourceFile>(useFile);
5547+
return !useSF || useSF->isImportedAsSPI(VD);
5548+
}
55405549
return true;
55415550
}
5551+
}
55425552
llvm_unreachable("bad access level");
55435553
}
55445554

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ UNINTERESTING_FEATURE(SameElementRequirements)
146146
UNINTERESTING_FEATURE(SendingArgsAndResults)
147147
UNINTERESTING_FEATURE(CheckImplementationOnly)
148148
UNINTERESTING_FEATURE(CheckImplementationOnlyStrict)
149+
UNINTERESTING_FEATURE(EnforceSPIOperatorGroup)
149150

150151
static bool findUnderscoredLifetimeAttr(Decl *decl) {
151152
auto hasUnderscoredLifetimeAttr = [](Decl *decl) {

test/SPI/Inputs/spi_helper.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ public protocol PublicProto {
44
associatedtype Assoc
55
}
66

7+
public struct PublicType {
8+
public init() { }
9+
}
10+
711
public func publicFunc() { print("publicFunc") }
812

913
func internalFunc() {}
@@ -94,3 +98,6 @@ public enum PublicEnum {
9498
case publicCase
9599
@_spi(HelperSPI) case spiCase
96100
}
101+
102+
@_spi(HelperSPI) public func -(_ s1: PublicType, _ s2: PublicType) -> PublicType { s1 }
103+
@_spi(HelperSPI) public func +(_ s1: PublicType, _ s2: PublicType) -> PublicType { s1 }

test/SPI/local_spi_decls.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,20 +115,32 @@ public func publicFuncWithDefaultValue(_ p: SPIClass = SPIClass()) {} // expecte
115115
@_spi(S)
116116
public func spiFuncWithDefaultValue(_ p: SPIClass = SPIClass()) {}
117117

118+
public struct PublicType {
119+
public init() { }
120+
}
121+
@_spi(S) public func -(_ s1: PublicType, _ s2: PublicType) -> PublicType { s1 }
122+
123+
public let o1 = PublicType()
124+
public let o2 = PublicType()
125+
118126
@inlinable
119127
public func inlinablePublic() {
120128
spiFunc() // expected-error {{global function 'spiFunc()' cannot be used in an '@inlinable' function because it is SPI}}
121129
let _ = SPIClass() // expected-error {{class 'SPIClass' cannot be used in an '@inlinable' function because it is SPI}}
122130
// expected-error@-1 {{initializer 'init()' cannot be used in an '@inlinable' function because it is SPI}}
131+
let _ = o1 - o2 // expected-error {{operator function '-' cannot be used in an '@inlinable' function because it is SPI}}
123132
}
124133

125134
@_spi(S)
126135
@inlinable
127136
public func inlinableSPI() {
128137
spiFunc()
129138
let _ = SPIClass()
139+
let _ = o1 - o2
130140
}
131141

132142
@_spi(S) func internalFunc() {} // expected-error {{internal global function cannot be declared '@_spi' because only public and open declarations can be '@_spi'}}
133143

134144
@_spi(S) package func packageFunc() {} // expected-error {{package global function cannot be declared '@_spi' because only public and open declarations can be '@_spi'}}
145+
146+
let _ = o1 - o2

test/SPI/private_swiftinterface.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,16 @@
2929

3030
/// Test the textual interfaces generated from this test.
3131
// 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
32-
// RUN: %FileCheck -check-prefix=CHECK-PUBLIC %s < %t/Main.swiftinterface
33-
// RUN: %FileCheck -check-prefix=CHECK-PRIVATE %s < %t/Main.private.swiftinterface
32+
// RUN: cat %t/Main.swiftinterface | %FileCheck -check-prefix=CHECK-PUBLIC %s
33+
// RUN: cat %t/Main.private.swiftinterface | %FileCheck -check-prefix=CHECK-PRIVATE %s
3434
// RUN: %target-swift-frontend -typecheck-module-from-interface -I %t %t/Main.swiftinterface
3535
// RUN: %target-swift-frontend -typecheck-module-from-interface -I %t %t/Main.private.swiftinterface -module-name Main
3636

3737
/// Both the public and private textual interfaces should have
3838
/// SPI information with `-library-level spi`.
3939
// RUN: %target-swift-frontend -typecheck %s -emit-module-interface-path %t/SPIModule.swiftinterface -emit-private-module-interface-path %t/SPIModule.private.swiftinterface -enable-library-evolution -swift-version 5 -I %t -module-name SPIModule -library-level spi
40-
// RUN: %FileCheck -check-prefix=CHECK-PRIVATE %s < %t/SPIModule.swiftinterface
41-
// RUN: %FileCheck -check-prefix=CHECK-PRIVATE %s < %t/SPIModule.private.swiftinterface
40+
// RUN: cat %t/SPIModule.swiftinterface | %FileCheck -check-prefix=CHECK-PRIVATE %s
41+
// RUN: cat %t/SPIModule.private.swiftinterface | %FileCheck -check-prefix=CHECK-PRIVATE %s
4242
// RUN: %target-swift-frontend -typecheck-module-from-interface -I %t %t/SPIModule.swiftinterface
4343
// RUN: %target-swift-frontend -typecheck-module-from-interface -I %t %t/SPIModule.private.swiftinterface -module-name SPIModule
4444

@@ -238,6 +238,11 @@ extension IOIPublicStruct : LocalPublicProto {}
238238
// CHECK-PRIVATE: @_spi(S) private var spiTypeInFrozen1
239239
}
240240

241+
public struct OpType {}
242+
@_spi(S) public func +(_ s1: OpType, _ s2: OpType) -> OpType { s1 }
243+
// CHECK-PRIVATE: @_spi(S) public func + (s1: {{.*}}.OpType, s2: {{.*}}.OpType) -> {{.*}}.OpType
244+
// CHECK-PUBLIC-NOT: func +
245+
241246
// The dummy conformance should be only in the private swiftinterface for
242247
// SPI extensions.
243248
@_spi(LocalSPI)

test/SPI/public_client.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@
66
// RUN: %target-swift-frontend -emit-module %S/Inputs/spi_helper.swift -module-name SPIHelper -emit-module-path %t/SPIHelper.swiftmodule -emit-module-interface-path %t/SPIHelper.swiftinterface -emit-private-module-interface-path %t/SPIHelper.private.swiftinterface -enable-library-evolution -swift-version 5 -parse-as-library
77

88
/// Reading from swiftmodule
9-
// RUN: %target-typecheck-verify-swift -verify-ignore-unrelated -I %t -verify-ignore-unknown
9+
// RUN: %target-typecheck-verify-swift -verify-ignore-unrelated -I %t -verify-ignore-unknown \
10+
// RUN: -enable-experimental-feature EnforceSPIOperatorGroup
1011

1112
/// Reading from .private.swiftinterface
1213
// RUN: rm %t/SPIHelper.swiftmodule
13-
// RUN: %target-typecheck-verify-swift -verify-ignore-unrelated -I %t -verify-ignore-unknown
14+
// RUN: %target-typecheck-verify-swift -verify-ignore-unrelated -I %t -verify-ignore-unknown \
15+
// RUN: -enable-experimental-feature EnforceSPIOperatorGroup
1416

1517
/// Reading from the public .swiftinterface should raise errors on missing
1618
/// declarations.
1719
// RUN: rm %t/SPIHelper.private.swiftinterface
1820
// RUN: not %target-swift-frontend -typecheck -I %t %s
1921

22+
// REQUIRES: swift_feature_EnforceSPIOperatorGroup
23+
2024
import SPIHelper
2125

2226
// Use the public API
@@ -40,8 +44,14 @@ otherApiFunc() // expected-error {{cannot find 'otherApiFunc' in scope}}
4044
public func publicUseOfSPI(param: SPIClass) -> SPIClass {} // expected-error 2{{cannot find type 'SPIClass' in scope}}
4145
public func publicUseOfSPI2() -> [SPIClass] {} // expected-error {{cannot find type 'SPIClass' in scope}}
4246

47+
public let o1 = PublicType()
48+
public let o2 = PublicType()
49+
4350
@inlinable
44-
func inlinable() -> SPIClass { // expected-error {{cannot find type 'SPIClass' in scope}}
51+
public func inlinable() -> SPIClass { // expected-error {{cannot find type 'SPIClass' in scope}}
4552
spiFunc() // expected-error {{cannot find 'spiFunc' in scope}}
4653
_ = SPIClass() // expected-error {{cannot find 'SPIClass' in scope}}
54+
let _ = o1 - o2 // expected-error {{binary operator '-' cannot be applied to two 'PublicType' operands}}
4755
}
56+
57+
let _ = o1 - o2 // expected-error {{binary operator '-' cannot be applied to two 'PublicType' operands}}

test/SPI/spi_client.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,16 @@ public func inlinable1() -> SPIClass { // expected-error {{class 'SPIClass' cann
6161
_ = [SPIClass]() // expected-error {{class 'SPIClass' cannot be used in an '@inlinable' function because it is an SPI imported from 'SPIHelper'}}
6262
}
6363

64+
public let o1 = PublicType()
65+
public let o2 = PublicType()
66+
6467
@_spi(S)
6568
@inlinable
6669
public func inlinable2() -> SPIClass {
6770
spiFunc()
6871
_ = SPIClass()
6972
_ = [SPIClass]()
73+
let _ = o1 - o2
7074
}
75+
76+
let _ = o1 - o2

0 commit comments

Comments
 (0)