Skip to content

[5.3][ModuleInterface] Print some implementation-only imports in the private interface #32902

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/Frontend/ModuleInterfaceSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ struct ModuleInterfaceOptions {

// Print SPI decls and attributes.
bool PrintSPIs = false;

/// Print imports with both @_implementationOnly and @_spi, only applies
/// when PrintSPIs is true.
bool ExperimentalSPIImports = false;
};

extern version::Version InterfaceFormatVersion;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,10 @@ def module_interface_preserve_types_as_written :
HelpText<"When emitting a module interface, preserve types as they were "
"written in the source">;

def experimental_spi_imports :
Flag<["-"], "experimental-spi-imports">,
HelpText<"Enable experimental support for SPI imports">;

def experimental_print_full_convention :
Flag<["-"], "experimental-print-full-convention">,
HelpText<"When emitting a module interface, emit additional @convention "
Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ static void ParseModuleInterfaceArgs(ModuleInterfaceOptions &Opts,
Args.hasArg(OPT_module_interface_preserve_types_as_written);
Opts.PrintFullConvention |=
Args.hasArg(OPT_experimental_print_full_convention);
Opts.ExperimentalSPIImports |=
Args.hasArg(OPT_experimental_spi_imports);
}

/// Save a copy of any flags marked as ModuleInterfaceOption, if running
Expand Down
27 changes: 25 additions & 2 deletions lib/Frontend/ModuleInterfaceSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,21 @@ static void printImports(raw_ostream &out,
allImportFilter |= ModuleDecl::ImportFilterKind::Private;
allImportFilter |= ModuleDecl::ImportFilterKind::SPIAccessControl;

// With -experimental-spi-imports:
// When printing the private swiftinterface file, print implementation-only
// imports only if they are also SPI. First, list all implementation-only
// imports and filter them later.
llvm::SmallSet<ModuleDecl::ImportedModule, 4,
ModuleDecl::OrderImportedModules> ioiImportSet;
if (Opts.PrintSPIs && Opts.ExperimentalSPIImports) {
allImportFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;

SmallVector<ModuleDecl::ImportedModule, 4> ioiImport;
M->getImportedModules(ioiImport,
ModuleDecl::ImportFilterKind::ImplementationOnly);
ioiImportSet.insert(ioiImport.begin(), ioiImport.end());
}

SmallVector<ModuleDecl::ImportedModule, 8> allImports;
M->getImportedModules(allImports, allImportFilter);
ModuleDecl::removeDuplicateImports(allImports);
Expand All @@ -124,13 +139,21 @@ static void printImports(raw_ostream &out,
continue;
}

llvm::SmallVector<Identifier, 4> spis;
M->lookupImportedSPIGroups(importedModule, spis);

// Only print implementation-only imports which have an SPI import.
if (ioiImportSet.count(import)) {
if (spis.empty())
continue;
out << "@_implementationOnly ";
}

if (publicImportSet.count(import))
out << "@_exported ";

// SPI attribute on imports
if (Opts.PrintSPIs) {
SmallVector<Identifier, 4> spis;
M->lookupImportedSPIGroups(importedModule, spis);
for (auto spiName : spis)
out << "@_spi(" << spiName << ") ";
}
Expand Down
26 changes: 26 additions & 0 deletions test/SPI/experimental_spi_imports_swiftinterface.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/// Test the textual interfaces generated with -experimental-spi-imports.

// RUN: %empty-directory(%t)

/// Generate 3 empty modules.
// RUN: touch %t/empty.swift
// RUN: %target-swift-frontend -emit-module %t/empty.swift -module-name ExperimentalImported -emit-module-path %t/ExperimentalImported.swiftmodule -swift-version 5 -enable-library-evolution
// RUN: %target-swift-frontend -emit-module %t/empty.swift -module-name IOIImported -emit-module-path %t/IOIImported.swiftmodule -swift-version 5 -enable-library-evolution
// RUN: %target-swift-frontend -emit-module %t/empty.swift -module-name SPIImported -emit-module-path %t/SPIImported.swiftmodule -swift-version 5 -enable-library-evolution

/// Test the generated swiftinterface.
// 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
// RUN: %FileCheck -check-prefix=CHECK-PUBLIC %s < %t/main.swiftinterface
// RUN: %FileCheck -check-prefix=CHECK-PRIVATE %s < %t/main.private.swiftinterface

@_spi(dummy) @_implementationOnly import ExperimentalImported
// CHECK-PUBLIC-NOT: import ExperimentalImported
// CHECK-PRIVATE: @_implementationOnly @_spi{{.*}} import ExperimentalImported

@_implementationOnly import IOIImported
// CHECK-PUBLIC-NOT: IOIImported
// CHECK-PRIVATE-NOT: IOIImported

@_spi(dummy) import SPIImported
// CHECK-PUBLIC: {{^}}import SPIImported
// CHECK-PRIVATE: @_spi{{.*}} import SPIImported
50 changes: 50 additions & 0 deletions test/SPI/experimental_spi_imports_type_check.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/// Test the use of implementation-only types with -experimental-spi-imports.

/// Build LibCore an internal module and LibPublic a public module using LibCore.
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -DLIB_CORE %s -module-name LibCore -emit-module-path %t/LibCore.swiftmodule -enable-library-evolution -swift-version 5
// 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

/// Test with the swiftmodule file, the compiler raises an error only when
/// LibCore isn't loaded by the client.
// RUN: %target-typecheck-verify-swift -DCLIENT -I %t
// RUN: %target-swift-frontend -typecheck %s -DCLIENT -DCLIENT_LOAD_CORE -I %t

/// Test with the private swiftinterface file, the compiler raises an error
/// only when LibCore isn't loaded by the client.
// RUN: rm %t/LibPublic.swiftmodule
// RUN: %target-typecheck-verify-swift -DCLIENT -I %t
// RUN: %target-swift-frontend -typecheck %s -DCLIENT -DCLIENT_LOAD_CORE -I %t

/// Test with the public swiftinterface file, the SPI is unknown.
// RUN: rm %t/LibPublic.private.swiftinterface
// RUN: %target-typecheck-verify-swift -DCLIENT -I %t
// RUN: %target-typecheck-verify-swift -DCLIENT -DCLIENT_LOAD_CORE -I %t

#if LIB_CORE

public struct CoreStruct {
public init() {}
public func coreMethod() {}
}


#elseif LIB_PUBLIC

@_spi(dummy) @_implementationOnly import LibCore

@_spi(A) public func SPIFunc() -> CoreStruct { return CoreStruct() }


#elseif CLIENT

@_spi(A) import LibPublic

#if CLIENT_LOAD_CORE
import LibCore
#endif

let x = SPIFunc() // expected-error {{cannot find 'SPIFunc' in scope}}
x.coreMethod()

#endif