Skip to content

Commit 0cce549

Browse files
committed
[ModuleInterface] Print some implementation-only imports in the private interface
Print implementation-only imports in the private textual interface only if also importing SPI. This allows to export types from implementation-only imports in SPI and brings the private textual interfaces in line with the binary interfaces. This is a temporary solution as we need to better design the language feature around this. This feature requires passing -experimental-spi-imports to the frontend that generates the private swiftinterface file.
1 parent 6334c34 commit 0cce549

File tree

6 files changed

+117
-6
lines changed

6 files changed

+117
-6
lines changed

include/swift/Frontend/ModuleInterfaceSupport.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ struct ModuleInterfaceOptions {
4343

4444
// Print SPI decls and attributes.
4545
bool PrintSPIs = false;
46+
47+
/// Print imports with both @_implementationOnly and @_spi, only applies
48+
/// when PrintSPIs is true.
49+
bool ExperimentalSPIImports = false;
4650
};
4751

4852
extern version::Version InterfaceFormatVersion;

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,10 @@ def module_interface_preserve_types_as_written :
632632
HelpText<"When emitting a module interface, preserve types as they were "
633633
"written in the source">;
634634

635+
def experimental_spi_imports :
636+
Flag<["-"], "experimental-spi-imports">,
637+
HelpText<"Enable experimental support for SPI imports">;
638+
635639
def experimental_print_full_convention :
636640
Flag<["-"], "experimental-print-full-convention">,
637641
HelpText<"When emitting a module interface, emit additional @convention "

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts,
313313
Args.hasArg(OPT_module_interface_preserve_types_as_written);
314314
Opts.PrintFullConvention |=
315315
Args.hasArg(OPT_experimental_print_full_convention);
316+
Opts.ExperimentalSPIImports |=
317+
Args.hasArg(OPT_experimental_spi_imports);
316318
}
317319

318320
/// Save a copy of any flags marked as ModuleInterfaceOption, if running

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,28 @@ static void printImports(raw_ostream &out,
100100
ModuleDecl *M) {
101101
// FIXME: This is very similar to what's in Serializer::writeInputBlock, but
102102
// it's not obvious what higher-level optimization would be factored out here.
103+
ModuleDecl::ImportFilter allImportFilter;
104+
allImportFilter |= ModuleDecl::ImportFilterKind::Public;
105+
allImportFilter |= ModuleDecl::ImportFilterKind::Private;
106+
allImportFilter |= ModuleDecl::ImportFilterKind::SPIAccessControl;
107+
108+
// With -experimental-spi-imports:
109+
// When printing the private swiftinterface file, print implementation-only
110+
// imports only if they are also SPI. First, list all implementation-only
111+
// imports and filter them later.
112+
llvm::SmallSet<ModuleDecl::ImportedModule, 4,
113+
ModuleDecl::OrderImportedModules> ioiImportSet;
114+
if (Opts.PrintSPIs && Opts.ExperimentalSPIImports) {
115+
allImportFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;
116+
117+
SmallVector<ModuleDecl::ImportedModule, 4> ioiImport;
118+
M->getImportedModules(ioiImport,
119+
ModuleDecl::ImportFilterKind::ImplementationOnly);
120+
ioiImportSet.insert(ioiImport.begin(), ioiImport.end());
121+
}
122+
103123
SmallVector<ModuleDecl::ImportedModule, 8> allImports;
104-
M->getImportedModules(allImports,
105-
{ModuleDecl::ImportFilterKind::Public,
106-
ModuleDecl::ImportFilterKind::Private,
107-
ModuleDecl::ImportFilterKind::SPIAccessControl});
124+
M->getImportedModules(allImports, allImportFilter);
108125
ModuleDecl::removeDuplicateImports(allImports);
109126
diagnoseScopedImports(M->getASTContext().Diags, allImports);
110127

@@ -124,13 +141,21 @@ static void printImports(raw_ostream &out,
124141
continue;
125142
}
126143

144+
llvm::SmallSetVector<Identifier, 4> spis;
145+
M->lookupImportedSPIGroups(importedModule, spis);
146+
147+
// Only print implementation-only imports which have an SPI import.
148+
if (ioiImportSet.count(import)) {
149+
if (spis.empty())
150+
continue;
151+
out << "@_implementationOnly ";
152+
}
153+
127154
if (publicImportSet.count(import))
128155
out << "@_exported ";
129156

130157
// SPI attribute on imports
131158
if (Opts.PrintSPIs) {
132-
llvm::SmallSetVector<Identifier, 4> spis;
133-
M->lookupImportedSPIGroups(importedModule, spis);
134159
for (auto spiName : spis)
135160
out << "@_spi(" << spiName << ") ";
136161
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// Test the textual interfaces generated with -experimental-spi-imports.
2+
3+
// RUN: %empty-directory(%t)
4+
5+
/// Generate 3 empty modules.
6+
// RUN: touch %t/empty.swift
7+
// RUN: %target-swift-frontend -emit-module %t/empty.swift -module-name ExperimentalImported -emit-module-path %t/ExperimentalImported.swiftmodule -swift-version 5 -enable-library-evolution
8+
// RUN: %target-swift-frontend -emit-module %t/empty.swift -module-name IOIImported -emit-module-path %t/IOIImported.swiftmodule -swift-version 5 -enable-library-evolution
9+
// RUN: %target-swift-frontend -emit-module %t/empty.swift -module-name SPIImported -emit-module-path %t/SPIImported.swiftmodule -swift-version 5 -enable-library-evolution
10+
11+
/// Test the generated swiftinterface.
12+
// 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 -experimental-spi-imports
13+
// RUN: %FileCheck -check-prefix=CHECK-PUBLIC %s < %t/main.swiftinterface
14+
// RUN: %FileCheck -check-prefix=CHECK-PRIVATE %s < %t/main.private.swiftinterface
15+
16+
@_spi(dummy) @_implementationOnly import ExperimentalImported
17+
// CHECK-PUBLIC-NOT: import ExperimentalImported
18+
// CHECK-PRIVATE: @_implementationOnly @_spi{{.*}} import ExperimentalImported
19+
20+
@_implementationOnly import IOIImported
21+
// CHECK-PUBLIC-NOT: IOIImported
22+
// CHECK-PRIVATE-NOT: IOIImported
23+
24+
@_spi(dummy) import SPIImported
25+
// CHECK-PUBLIC: {{^}}import SPIImported
26+
// CHECK-PRIVATE: @_spi{{.*}} import SPIImported
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/// Test the use of implementation-only types with -experimental-spi-imports.
2+
3+
/// Build LibCore an internal module and LibPublic a public module using LibCore.
4+
// RUN: %empty-directory(%t)
5+
// RUN: %target-swift-frontend -emit-module -DLIB_CORE %s -module-name LibCore -emit-module-path %t/LibCore.swiftmodule -enable-library-evolution -swift-version 5
6+
// RUN: %target-swift-frontend -emit-module -DLIB_PUBLIC %s -module-name LibPublic -emit-module-path %t/LibPublic.swiftmodule -I %t -emit-module-interface-path %t/LibPublic.swiftinterface -emit-private-module-interface-path %t/LibPublic.private.swiftinterface -enable-library-evolution -swift-version 5 -experimental-spi-imports
7+
8+
/// Test with the swiftmodule file, the compiler raises an error only when
9+
/// LibCore isn't loaded by the client.
10+
// RUN: %target-typecheck-verify-swift -DCLIENT -I %t
11+
// RUN: %target-swift-frontend -typecheck %s -DCLIENT -DCLIENT_LOAD_CORE -I %t
12+
13+
/// Test with the private swiftinterface file, the compiler raises an error
14+
/// only when LibCore isn't loaded by the client.
15+
// RUN: rm %t/LibPublic.swiftmodule
16+
// RUN: %target-typecheck-verify-swift -DCLIENT -I %t
17+
// RUN: %target-swift-frontend -typecheck %s -DCLIENT -DCLIENT_LOAD_CORE -I %t
18+
19+
/// Test with the public swiftinterface file, the SPI is unknown.
20+
// RUN: rm %t/LibPublic.private.swiftinterface
21+
// RUN: %target-typecheck-verify-swift -DCLIENT -I %t
22+
// RUN: %target-typecheck-verify-swift -DCLIENT -DCLIENT_LOAD_CORE -I %t
23+
24+
#if LIB_CORE
25+
26+
public struct CoreStruct {
27+
public init() {}
28+
public func coreMethod() {}
29+
}
30+
31+
32+
#elseif LIB_PUBLIC
33+
34+
@_spi(dummy) @_implementationOnly import LibCore
35+
36+
@_spi(A) public func SPIFunc() -> CoreStruct { return CoreStruct() }
37+
38+
39+
#elseif CLIENT
40+
41+
@_spi(A) import LibPublic
42+
43+
#if CLIENT_LOAD_CORE
44+
import LibCore
45+
#endif
46+
47+
let x = SPIFunc() // expected-error {{cannot find 'SPIFunc' in scope}}
48+
x.coreMethod()
49+
50+
#endif

0 commit comments

Comments
 (0)