Skip to content

Commit c0abde0

Browse files
committed
[Sema] @_exported imports export @_spi decls too
Enable transitive imports of all SPI groups through @_exported imports. This brings to SPI the same behavior that we have for API. ``` // Module A @_spi(S) public func foo() {} // Module B @_exported import A // Module C @_spi(S) import B foo() // SPI imported through the reexport of A from B ``` rdar://101566534
1 parent 3ca1de0 commit c0abde0

File tree

3 files changed

+154
-2
lines changed

3 files changed

+154
-2
lines changed

lib/AST/Module.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2849,9 +2849,11 @@ canBeUsedForCrossModuleOptimization(DeclContext *ctxt) const {
28492849
void SourceFile::lookupImportedSPIGroups(
28502850
const ModuleDecl *importedModule,
28512851
llvm::SmallSetVector<Identifier, 4> &spiGroups) const {
2852+
auto &imports = getASTContext().getImportCache();
28522853
for (auto &import : *Imports) {
28532854
if (import.options.contains(ImportFlags::SPIAccessControl) &&
2854-
importedModule == import.module.importedModule) {
2855+
(importedModule == import.module.importedModule ||
2856+
imports.isImportedBy(importedModule, import.module.importedModule))) {
28552857
spiGroups.insert(import.spiGroups.begin(), import.spiGroups.end());
28562858
}
28572859
}

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1488,11 +1488,13 @@ void SerializedASTFile::lookupImportedSPIGroups(
14881488
const ModuleDecl *importedModule,
14891489
llvm::SmallSetVector<Identifier, 4> &spiGroups) const {
14901490
auto M = getParentModule();
1491+
auto &imports = M->getASTContext().getImportCache();
14911492
for (auto &dep : File.Dependencies) {
14921493
if (!dep.Import.hasValue())
14931494
continue;
14941495

1495-
if (dep.Import->importedModule == importedModule) {
1496+
if (dep.Import->importedModule == importedModule ||
1497+
imports.isImportedBy(importedModule, dep.Import->importedModule)) {
14961498
spiGroups.insert(dep.spiGroups.begin(), dep.spiGroups.end());
14971499
}
14981500
}

test/SPI/reexported-spi-groups.swift

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
/// Build lib defining SPIs
5+
// RUN: %target-swift-frontend -emit-module %t/Exported.swift \
6+
// RUN: -module-name Exported -swift-version 5 \
7+
// RUN: -enable-library-evolution \
8+
// RUN: -emit-module-path %t/Exported.swiftmodule \
9+
// RUN: -emit-module-interface-path %t/Exported.swiftinterface \
10+
// RUN: -emit-private-module-interface-path %t/Exported.private.swiftinterface
11+
// RUN: %target-swift-typecheck-module-from-interface(%t/Exported.swiftinterface)
12+
// RUN: %target-swift-typecheck-module-from-interface(%t/Exported.private.swiftinterface) -module-name Exported
13+
14+
/// Build lib reexporting SPIs
15+
// RUN: %target-swift-frontend -emit-module %t/Exporter.swift \
16+
// RUN: -module-name Exporter -swift-version 5 -I %t \
17+
// RUN: -enable-library-evolution \
18+
// RUN: -emit-module-path %t/Exporter.swiftmodule \
19+
// RUN: -emit-module-interface-path %t/Exporter.swiftinterface \
20+
// RUN: -emit-private-module-interface-path %t/Exporter.private.swiftinterface
21+
// RUN: %target-swift-typecheck-module-from-interface(%t/Exporter.swiftinterface) -I %t
22+
// RUN: %target-swift-typecheck-module-from-interface(%t/Exporter.private.swiftinterface) -module-name Exporter -I %t
23+
24+
/// Build lib not reexporting SPIs (a normal import)
25+
// RUN: %target-swift-frontend -emit-module %t/NonExporter.swift \
26+
// RUN: -module-name NonExporter -swift-version 5 -I %t \
27+
// RUN: -enable-library-evolution \
28+
// RUN: -emit-module-path %t/NonExporter.swiftmodule \
29+
// RUN: -emit-module-interface-path %t/NonExporter.swiftinterface \
30+
// RUN: -emit-private-module-interface-path %t/NonExporter.private.swiftinterface
31+
// RUN: %target-swift-typecheck-module-from-interface(%t/NonExporter.swiftinterface) -I %t
32+
// RUN: %target-swift-typecheck-module-from-interface(%t/NonExporter.private.swiftinterface) -module-name NonExporter -I %t
33+
34+
/// Build client of transitive SPIs and its swiftinterfaces
35+
// RUN: %target-swift-frontend -emit-module %t/ClientLib.swift \
36+
// RUN: -module-name ClientLib -swift-version 5 -I %t \
37+
// RUN: -enable-library-evolution \
38+
// RUN: -emit-module-path %t/ClientLib.swiftmodule \
39+
// RUN: -emit-module-interface-path %t/ClientLib.swiftinterface \
40+
// RUN: -emit-private-module-interface-path %t/ClientLib.private.swiftinterface
41+
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.swiftinterface) -I %t
42+
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.private.swiftinterface) -module-name ClientLib -I %t
43+
44+
/// Test diagnostics of a multifile client
45+
// RUN: %target-swift-frontend -typecheck \
46+
// RUN: %t/Client_FileA.swift %t/Client_FileB.swift\
47+
// RUN: -swift-version 5 -I %t -verify
48+
49+
/// Test that SPIs don't leak when not reexported
50+
// RUN: %target-swift-frontend -typecheck \
51+
// RUN: %t/NonExporterClient.swift \
52+
// RUN: -swift-version 5 -I %t -verify
53+
54+
/// Test diagnostics against private swiftinterfaces
55+
// RUN: rm %t/Exported.swiftmodule %t/Exporter.swiftmodule
56+
// RUN: %target-swift-frontend -typecheck \
57+
// RUN: %t/Client_FileA.swift %t/Client_FileB.swift\
58+
// RUN: -swift-version 5 -I %t -verify
59+
60+
/// Test diagnostics against public swiftinterfaces
61+
// RUN: rm %t/Exported.private.swiftinterface %t/Exporter.private.swiftinterface
62+
// RUN: %target-swift-frontend -typecheck \
63+
// RUN: %t/PublicClient.swift \
64+
// RUN: -swift-version 5 -I %t -verify
65+
66+
67+
//--- Exported.swift
68+
69+
public func exportedPublicFunc() {}
70+
71+
@_spi(X) public func exportedSpiFunc() {}
72+
73+
@_spi(X) public struct ExportedSpiType {}
74+
75+
//--- Exporter.swift
76+
77+
@_exported import Exported
78+
79+
@_spi(X) public func exporterSpiFunc() {}
80+
81+
//--- NonExporter.swift
82+
83+
@_spi(X) import Exported
84+
85+
@_spi(X) public func exporterSpiFunc() {}
86+
87+
//--- ClientLib.swift
88+
89+
@_spi(X) import Exporter
90+
91+
public func clientA() {
92+
exportedPublicFunc()
93+
exportedSpiFunc()
94+
exporterSpiFunc()
95+
}
96+
97+
@_spi(X) public func spiUseExportedSpiType(_ a: ExportedSpiType) {}
98+
99+
//--- Client_FileA.swift
100+
101+
@_spi(X) import Exporter
102+
103+
public func clientA() {
104+
exportedPublicFunc()
105+
exportedSpiFunc()
106+
exporterSpiFunc()
107+
}
108+
109+
@inlinable
110+
public func inlinableClient() {
111+
exportedPublicFunc()
112+
exportedSpiFunc() // expected-error {{global function 'exportedSpiFunc()' cannot be used in an '@inlinable' function because it is an SPI imported from 'Exported'}}
113+
exporterSpiFunc() // expected-error {{global function 'exporterSpiFunc()' cannot be used in an '@inlinable' function because it is an SPI imported from 'Exporter'}}
114+
}
115+
116+
@_spi(X) public func spiUseExportedSpiType(_ a: ExportedSpiType) {}
117+
118+
public func publicUseExportedSpiType(_ a: ExportedSpiType) {} // expected-error {{cannot use struct 'ExportedSpiType' here; it is an SPI imported from 'Exported'}}
119+
120+
//--- Client_FileB.swift
121+
122+
import Exporter
123+
124+
public func clientB() {
125+
exportedPublicFunc()
126+
exportedSpiFunc() // expected-error {{cannot find 'exportedSpiFunc' in scope}}
127+
exporterSpiFunc() // expected-error {{cannot find 'exporterSpiFunc' in scope}}
128+
}
129+
130+
//--- NonExporterClient.swift
131+
132+
@_spi(X) import NonExporter
133+
134+
public func client() {
135+
exportedPublicFunc() // expected-error {{cannot find 'exportedPublicFunc' in scope}}
136+
exportedSpiFunc() // expected-error {{cannot find 'exportedSpiFunc' in scope}}
137+
exporterSpiFunc()
138+
}
139+
140+
//--- PublicClient.swift
141+
142+
@_spi(X) import Exporter // expected-warning {{'@_spi' import of 'Exporter' will not include any SPI symbols}}
143+
144+
public func client() {
145+
exportedPublicFunc()
146+
exportedSpiFunc() // expected-error {{cannot find 'exportedSpiFunc' in scope}}
147+
exporterSpiFunc() // expected-error {{cannot find 'exporterSpiFunc' in scope}}
148+
}

0 commit comments

Comments
 (0)