Merge pull request #63248 from xymus/swift-export-as

[ModuleInterface] Intro the flag `-export-as` for Swift modules
This commit is contained in:
Alexis Laferrière
2023-01-27 21:23:35 -08:00
committed by GitHub
17 changed files with 165 additions and 3 deletions

View File

@@ -180,6 +180,11 @@ ERROR(error_stdlib_module_name,none,
"module name \"%0\" is reserved for the standard library"
"%select{|; use -module-name flag to specify an alternate name}1",
(StringRef, bool))
ERROR(error_bad_export_as_name,none,
"export-as name \"%0\" is not a valid identifier",
(StringRef))
ERROR(error_bad_package_name,none,
"package name \"%0\" is not a valid identifier",
(StringRef))

View File

@@ -322,8 +322,11 @@ public:
/// The 'real name' is the actual binary name of the module, which can be different from the 'name'
/// if module aliasing was used (via -module-alias flag).
///
/// Usually this is the module real name itself, but certain Clang features allow
/// substituting another name instead.
/// This is usually the module real name which can be overriden by an
/// `export_as` definition of a clang module, or `-export-as` flag on an
/// imported Swift module. Swift modules built from source do not apply
/// their own `-export-as` flag, this way the swiftinterface can be
/// verified.
virtual StringRef getExportedModuleName() const {
return getParentModule()->getRealName().str();
}

View File

@@ -175,6 +175,10 @@ class ModuleDecl
/// The name of the package this module belongs to
mutable Identifier PackageName;
/// Module name to use when referenced in clients module interfaces.
mutable Identifier ExportAsName;
public:
/// Produces the components of a given module's full name in reverse order.
///
@@ -410,6 +414,12 @@ public:
PackageName = name;
}
Identifier getExportAsName() const { return ExportAsName; }
void setExportAsName(Identifier name) {
ExportAsName = name;
}
/// Retrieve the actual module name of an alias used for this module (if any).
///
/// For example, if '-module-alias Foo=Bar' is passed in when building the main module,

View File

@@ -67,6 +67,9 @@ public:
/// The name of the package this module belongs to.
std::string PackageName;
/// Module name to use when referenced in clients module interfaces.
std::string ExportAsName;
/// Arguments which should be passed in immediate mode.
std::vector<std::string> ImmediateArgv;

View File

@@ -484,6 +484,9 @@ def module_abi_name : Separate<["-"], "module-abi-name">,
def package_name : Separate<["-"], "package-name">,
Flags<[FrontendOption, ModuleInterfaceOptionIgnorable]>,
HelpText<"Name of the package the module belongs to">;
def export_as : Separate<["-"], "export-as">,
Flags<[FrontendOption, ModuleInterfaceOptionIgnorable]>,
HelpText<"Module name to use when referenced in clients module interfaces">;
def emit_module : Flag<["-"], "emit-module">,
Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>,

View File

@@ -455,6 +455,8 @@ public:
virtual StringRef getModuleDefiningPath() const override;
virtual StringRef getExportedModuleName() const override;
ValueDecl *getMainDecl() const override;
bool hasEntryPoint() const override;

View File

@@ -108,6 +108,7 @@ class ExtendedValidationInfo {
std::string SDKPath;
StringRef ModuleABIName;
StringRef ModulePackageName;
StringRef ExportAsName;
struct {
unsigned ArePrivateImportsEnabled : 1;
unsigned IsSIB : 1;
@@ -183,6 +184,9 @@ public:
StringRef getModulePackageName() const { return ModulePackageName; }
void setModulePackageName(StringRef name) { ModulePackageName = name; }
StringRef getExportAsName() const { return ExportAsName; }
void setExportAsName(StringRef name) { ExportAsName = name; }
bool isConcurrencyChecked() const {
return Bits.IsConcurrencyChecked;
}

View File

@@ -253,6 +253,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
inputArgs.AddLastArg(arguments, options::OPT_module_link_name);
inputArgs.AddLastArg(arguments, options::OPT_module_abi_name);
inputArgs.AddLastArg(arguments, options::OPT_package_name);
inputArgs.AddLastArg(arguments, options::OPT_export_as);
inputArgs.AddLastArg(arguments, options::OPT_nostdimport);
inputArgs.AddLastArg(arguments, options::OPT_parse_stdlib);
inputArgs.AddLastArg(arguments, options::OPT_resource_dir);

View File

@@ -279,6 +279,14 @@ bool ArgsToFrontendOptionsConverter::convert(
Opts.PackageName = pkgName;
}
if (const Arg *A = Args.getLastArg(OPT_export_as)) {
auto exportAs = A->getValue();
if (!Lexer::isIdentifier(exportAs))
Diags.diagnose(SourceLoc(), diag::error_bad_export_as_name, exportAs);
else
Opts.ExportAsName = exportAs;
}
// This must be called after computing module name, module abi name,
// and module link name. If computing module aliases is unsuccessful,
// return early.

View File

@@ -1085,6 +1085,10 @@ ModuleDecl *CompilerInstance::getMainModule() const {
MainModule->setPackageName(getASTContext().getIdentifier(
Invocation.getFrontendOptions().PackageName));
}
if (!Invocation.getFrontendOptions().ExportAsName.empty()) {
MainModule->setExportAsName(getASTContext().getIdentifier(
Invocation.getFrontendOptions().ExportAsName));
}
if (Invocation.getFrontendOptions().EnableLibraryEvolution)
MainModule->setResilienceStrategy(ResilienceStrategy::Resilient);
if (Invocation.getLangOptions().isSwiftVersionAtLeast(6))

View File

@@ -1324,3 +1324,10 @@ StringRef SerializedASTFile::getModuleDefiningPath() const {
return moduleFilename;
}
StringRef SerializedASTFile::getExportedModuleName() const {
auto name = File.getModuleExportAsName();
if (!name.empty())
return name;
return FileUnit::getExportedModuleName();
}

View File

@@ -535,6 +535,11 @@ public:
StringRef getModulePackageName() const {
return Core->ModulePackageName;
}
StringRef getModuleExportAsName() const {
return Core->ModuleExportAsName;
}
/// The ABI name of the module.
StringRef getModuleABIName() const {
return Core->ModuleABIName;

View File

@@ -166,6 +166,9 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor,
case options_block::MODULE_PACKAGE_NAME:
extendedInfo.setModulePackageName(blobData);
break;
case options_block::MODULE_EXPORT_AS_NAME:
extendedInfo.setExportAsName(blobData);
break;
default:
// Unknown options record, possibly for use by a future version of the
// module format.
@@ -1350,6 +1353,7 @@ ModuleFileSharedCore::ModuleFileSharedCore(
MiscVersion = info.miscVersion;
ModuleABIName = extInfo.getModuleABIName();
ModulePackageName = extInfo.getModulePackageName();
ModuleExportAsName = extInfo.getExportAsName();
hasValidControlBlock = true;
break;

View File

@@ -85,6 +85,9 @@ class ModuleFileSharedCore {
/// The name of the package this module belongs to.
StringRef ModulePackageName;
/// Module name to use when referenced in clients module interfaces.
StringRef ModuleExportAsName;
/// \c true if this module has incremental dependency information.
bool HasIncrementalInfo = false;

View File

@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 739; // CxxStdlib
const uint16_t SWIFTMODULE_VERSION_MINOR = 740; // export-as
/// A standard hash seed used for all string hashes in a serialized module.
///
@@ -861,6 +861,7 @@ namespace options_block {
MODULE_ABI_NAME,
IS_CONCURRENCY_CHECKED,
MODULE_PACKAGE_NAME,
MODULE_EXPORT_AS_NAME,
};
using SDKPathLayout = BCRecordLayout<
@@ -924,6 +925,11 @@ namespace options_block {
MODULE_PACKAGE_NAME,
BCBlob
>;
using ModuleExportAsNameLayout = BCRecordLayout<
MODULE_EXPORT_AS_NAME,
BCBlob
>;
}
/// The record types within the input block.

View File

@@ -1073,6 +1073,11 @@ void Serializer::writeHeader(const SerializationOptions &options) {
PackageName.emit(ScratchRecord, M->getPackageName().str());
}
if (!M->getExportAsName().empty()) {
options_block::ModuleExportAsNameLayout ExportAs(Out);
ExportAs.emit(ScratchRecord, M->getExportAsName().str());
}
if (M->isConcurrencyChecked()) {
options_block::IsConcurrencyCheckedLayout IsConcurrencyChecked(Out);
IsConcurrencyChecked.emit(ScratchRecord);

View File

@@ -0,0 +1,89 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
// RUN: %target-swift-frontend -emit-module %t/PrivateLib.swift \
// RUN: -swift-version 5 -enable-library-evolution \
// RUN: -export-as PublicLib \
// RUN: -o %t/PrivateLib.swiftmodule \
// RUN: -emit-module-interface-path %t/PrivateLib.swiftinterface \
// RUN: -emit-private-module-interface-path %t/PrivateLib.private.swiftinterface
// RUN: %target-swift-typecheck-module-from-interface(%t/PrivateLib.swiftinterface)
// RUN: %target-swift-typecheck-module-from-interface(%t/PrivateLib.private.swiftinterface) \
// RUN: -module-name PrivateLib
// RUN: cat %t/PrivateLib.swiftinterface | %FileCheck --check-prefixes=PRIVATELIB-PUBLIC %s
// RUN: cat %t/PrivateLib.private.swiftinterface | %FileCheck --check-prefixes=PRIVATELIB-PUBLIC %s
// RUN: %target-swift-frontend -emit-module %t/PublicLib.swift -I %t \
// RUN: -swift-version 5 -enable-library-evolution \
// RUN: -o %t/PublicLib.swiftmodule \
// RUN: -emit-module-interface-path %t/PublicLib.swiftinterface \
// RUN: -emit-private-module-interface-path %t/PublicLib.private.swiftinterface
// RUN: %target-swift-typecheck-module-from-interface(%t/PublicLib.swiftinterface) -I %t
// RUN: %target-swift-typecheck-module-from-interface(%t/PublicLib.private.swiftinterface) -I %t \
// RUN: -module-name PublicLib
// RUN: cat %t/PublicLib.swiftinterface | %FileCheck --check-prefixes=PUBLICLIB-PUBLIC %s
// RUN: cat %t/PublicLib.private.swiftinterface | %FileCheck --check-prefixes=PUBLICLIB-PUBLIC %s
/// Default logic applying export-as in both swiftinterface.
// RUN: %target-swift-frontend -emit-module %t/ClientLib.swift -I %t \
// RUN: -swift-version 5 -enable-library-evolution \
// RUN: -o %t/ClientLib.swiftmodule \
// RUN: -emit-module-interface-path %t/ClientLib.swiftinterface \
// RUN: -emit-private-module-interface-path %t/ClientLib.private.swiftinterface
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.swiftinterface) -I %t
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.private.swiftinterface) -I %t
// RUN: cat %t/ClientLib.swiftinterface | %FileCheck --check-prefixes=CLIENT-PUBLIC %s
// RUN: cat %t/ClientLib.private.swiftinterface | %FileCheck --check-prefixes=CLIENT-PUBLIC %s
/// New logic applying export-as only in the public swiftinterface with
/// `-enable-experimental-feature ModuleInterfaceExportAs`.
// RUN: %target-swift-frontend -emit-module %t/ClientLib.swift -I %t \
// RUN: -swift-version 5 -enable-library-evolution \
// RUN: -o %t/ClientLib.swiftmodule \
// RUN: -emit-module-interface-path %t/ClientLib.swiftinterface \
// RUN: -emit-private-module-interface-path %t/ClientLib.private.swiftinterface \
// RUN: -enable-experimental-feature ModuleInterfaceExportAs
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.swiftinterface) -I %t
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.private.swiftinterface) -I %t
// RUN: cat %t/ClientLib.swiftinterface | %FileCheck --check-prefixes=CLIENT-PUBLIC %s
// RUN: cat %t/ClientLib.private.swiftinterface | %FileCheck --check-prefixes=CLIENT-PRIVATE %s
/// Check that we get the same behavior using swiftinterfaces only.
// RUN: rm -f %t/PrivateLib.swiftmodule %t/PublicLib.swiftmodule
// RUN: %target-swift-frontend -emit-module %t/ClientLib.swift -I %t \
// RUN: -swift-version 5 -enable-library-evolution \
// RUN: -o %t/ClientLib.swiftmodule \
// RUN: -emit-module-interface-path %t/ClientLib.swiftinterface \
// RUN: -emit-private-module-interface-path %t/ClientLib.private.swiftinterface \
// RUN: -enable-experimental-feature ModuleInterfaceExportAs
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.swiftinterface) -I %t
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.private.swiftinterface) -I %t
// RUN: cat %t/ClientLib.swiftinterface | %FileCheck --check-prefixes=CLIENT-PUBLIC %s
// RUN: cat %t/ClientLib.private.swiftinterface | %FileCheck --check-prefixes=CLIENT-PRIVATE %s
//--- PrivateLib.swift
public struct PrivateNameStruct {}
public func privateLibUser(_ arg: PrivateNameStruct) {}
// PRIVATELIB-PUBLIC: arg: PrivateLib.PrivateNameStruct
//--- PublicLib.swift
@_exported import PrivateLib
public struct PublicNameStruct {}
public func publicLibUser(_ arg: PrivateNameStruct) {}
// PUBLICLIB-PUBLIC: arg: PublicLib.PrivateNameStruct
//--- ClientLib.swift
import PublicLib
public func userOfPrivate(_ argUserOfPrivate: PrivateNameStruct) {}
// CLIENT-PUBLIC: argUserOfPrivate: PublicLib.PrivateNameStruct
// CLIENT-PRIVATE: argUserOfPrivate: PrivateLib.PrivateNameStruct
public func userOfPublic(_ argUserOfPublic: PublicNameStruct) {}
// CLIENT-PUBLIC: argUserOfPublic: PublicLib.PublicNameStruct
// CLIENT-PRIVATE: argUserOfPublic: PublicLib.PublicNameStruct