[Serialization] Preserve submodule import paths through serialization.

This is a bit of a hack, but Clang submodules are the only case we have to
support.

Part of <rdar://problem/13140302>

Swift SVN r20287
This commit is contained in:
Jordan Rose
2014-07-22 01:31:15 +00:00
parent d82edd9608
commit 6ee53ff75f
7 changed files with 87 additions and 33 deletions

View File

@@ -81,16 +81,18 @@ public:
private:
const unsigned IsExported : 1;
const unsigned IsHeader : 1;
const unsigned IsScoped : 1;
Dependency(StringRef path, bool exported, bool isHeader)
: RawPath(path), IsExported(exported), IsHeader(isHeader){}
Dependency(StringRef path, bool isHeader, bool exported, bool isScoped)
: RawPath(path), IsExported(exported), IsHeader(isHeader),
IsScoped(isScoped) {}
public:
Dependency(StringRef path, bool exported)
: Dependency(path, exported, false) {}
Dependency(StringRef path, bool exported, bool isScoped)
: Dependency(path, false, exported, isScoped) {}
static Dependency forHeader(StringRef headerPath, bool exported) {
return Dependency(headerPath, exported, true);
return Dependency(headerPath, true, exported, false);
}
bool isLoaded() const {
@@ -99,6 +101,9 @@ public:
bool isExported() const { return IsExported; }
bool isHeader() const { return IsHeader; }
bool isScoped() const { return IsScoped; }
std::string getPrettyPrintedPath() const;
};
private:
@@ -505,7 +510,7 @@ public:
///
/// If the name matches the name of the current module, a shadowed module
/// is loaded instead.
Module *getModule(Identifier name);
Module *getModule(ArrayRef<Identifier> name);
/// Reads a substitution record from \c DeclTypeCursor.
///

View File

@@ -355,7 +355,10 @@ namespace input_block {
using ImportedModuleLayout = BCRecordLayout<
IMPORTED_MODULE,
BCFixed<1>, // exported?
BCBlob // module name, optionally followed by a null and then an import path
BCFixed<1>, // scoped?
BCBlob // module name, with submodule path pieces separated by \0s.
// If the 'scoped' flag is set, the final path piece is an access
// path within the module.
>;
using LinkLibraryLayout = BCRecordLayout<

View File

@@ -1347,25 +1347,27 @@ Module *ModuleFile::getModule(ModuleID MID) {
return getModule(getIdentifier(MID));
}
Module *ModuleFile::getModule(Identifier name) {
if (name.empty())
Module *ModuleFile::getModule(ArrayRef<Identifier> name) {
if (name.empty() || name.front().empty())
return getContext().TheBuiltinModule;
// FIXME: duplicated from NameBinder::getModule
// FIXME: provide a real source location.
if (name == FileContext->getParentModule()->Name) {
if (name.size() == 1 &&
name.front() == FileContext->getParentModule()->Name) {
if (!ShadowedModule) {
auto importer = getContext().getClangModuleLoader();
assert(importer && "no way to import shadowed module");
ShadowedModule = importer->loadModule(SourceLoc(),
std::make_pair(name, SourceLoc()));
{ { name.front(), SourceLoc() } });
}
return ShadowedModule;
}
// FIXME: provide a real source location.
return getContext().getModule(std::make_pair(name, SourceLoc()));
SmallVector<ImportDecl::AccessPathElement, 4> importPath;
for (auto pathElem : name)
importPath.push_back({ pathElem, SourceLoc() });
return getContext().getModule(importPath);
}

View File

@@ -181,6 +181,12 @@ SerializedModuleLoader::validateSerializedAST(StringRef data) {
return result;
}
std::string ModuleFile::Dependency::getPrettyPrintedPath() const {
std::string output = RawPath.str();
std::replace(output.begin(), output.end(), '\0', '.');
return output;
}
namespace {
class PrettyModuleFileDeserialization : public llvm::PrettyStackTraceEntry {
const ModuleFile &File;
@@ -579,9 +585,10 @@ ModuleFile::ModuleFile(
SourcePaths.push_back(blobData);
break;
case input_block::IMPORTED_MODULE: {
bool exported;
input_block::ImportedModuleLayout::readRecord(scratch, exported);
Dependencies.push_back({blobData, exported});
bool exported, scoped;
input_block::ImportedModuleLayout::readRecord(scratch,
exported, scoped);
Dependencies.push_back({blobData, exported, scoped});
break;
}
case input_block::LINK_LIBRARY: {
@@ -803,16 +810,28 @@ bool ModuleFile::associateWithFileContext(FileUnit *file) {
continue;
}
StringRef modulePath, scopePath;
std::tie(modulePath, scopePath) = dependency.RawPath.split('\0');
StringRef modulePathStr = dependency.RawPath;
StringRef scopePath;
if (dependency.isScoped()) {
auto splitPoint = modulePathStr.find_last_of('\0');
assert(splitPoint != StringRef::npos);
scopePath = modulePathStr.substr(splitPoint+1);
modulePathStr = modulePathStr.slice(0, splitPoint);
}
auto moduleID = ctx.getIdentifier(modulePath);
assert(!moduleID.empty() &&
SmallVector<Identifier, 4> modulePath;
while (!modulePathStr.empty()) {
StringRef nextComponent;
std::tie(nextComponent, modulePathStr) = modulePathStr.split('\0');
modulePath.push_back(ctx.getIdentifier(nextComponent));
assert(!modulePath.back().empty() &&
"invalid module name (submodules not yet supported)");
auto module = getModule(moduleID);
}
auto module = getModule(modulePath);
if (!module) {
// If we're missing the module we're shadowing, treat that specially.
if (moduleID == file->getParentModule()->Name) {
if (modulePath.size() == 1 &&
modulePath.front() == file->getParentModule()->Name) {
error(ModuleStatus::MissingShadowedModule);
return false;
}

View File

@@ -25,8 +25,11 @@
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/SourceManager.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Serialization/BCRecordLayout.h"
#include "clang/Basic/Module.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Bitcode/BitstreamWriter.h"
@@ -400,8 +403,23 @@ removeDuplicateImports(SmallVectorImpl<Module::ImportedModule> &imports) {
using ImportPathBlob = llvm::SmallString<64>;
static void flattenImportPath(const Module::ImportedModule &import,
ImportPathBlob &out) {
// FIXME: Submodules?
ArrayRef<FileUnit *> files = import.second->getFiles();
if (auto clangModule = dyn_cast<ClangModuleUnit>(files.front())) {
// FIXME: This is an awful hack to handle Clang submodules.
// Once Swift has a native notion of submodules, this can go away.
const clang::Module *submodule = clangModule->getClangModule();
SmallVector<StringRef, 4> submoduleNames;
do {
submoduleNames.push_back(submodule->Name);
submodule = submodule->Parent;
} while (submodule);
std::reverse(submoduleNames.begin(), submoduleNames.end());
interleave(submoduleNames,
[&out](StringRef next) { out.append(next); },
[&out] { out.push_back('\0'); });
} else {
out.append(import.second->Name.str());
}
if (import.first.empty())
return;
@@ -474,7 +492,7 @@ void Serializer::writeInputFiles(FilenamesTy inputFiles,
ImportPathBlob importPath;
flattenImportPath(import, importPath);
ImportedModule.emit(ScratchRecord, publicImportSet.count(import),
importPath);
!import.first.empty(), importPath);
}
if (!moduleLinkName.empty()) {

View File

@@ -243,13 +243,13 @@ FileUnit *SerializedModuleLoader::loadAST(
assert(!missing.empty() && "unknown missing dependency?");
if (missing.size() == 1) {
Ctx.Diags.diagnose(*diagLoc, diag::serialization_missing_single_dependency,
missing.begin()->RawPath);
missing.front().getPrettyPrintedPath());
} else {
llvm::SmallString<64> missingNames;
missingNames += '\'';
interleave(missing,
[&](const ModuleFile::Dependency &next) {
missingNames += next.RawPath;
missingNames += next.getPrettyPrintedPath();
},
[&] { missingNames += "', '"; });
missingNames += '\'';

View File

@@ -1,11 +1,18 @@
// RUN: rm -rf %t/clang-module-cache
// RUN: %swift %clang-importer-sdk -parse -verify -module-cache-path %t/clang-module-cache -target x86_64-apple-macosx10.9 %s
// RUN: rm -rf %t && mkdir -p %t
// RUN: %swift %clang-importer-sdk -parse -verify -module-cache-path %t/clang-module-cache %s
// RUN: ls -lR %t/clang-module-cache | FileCheck %s
// CHECK: ctypes{{.*}}.pcm
// RUN: %swift %clang-importer-sdk -emit-module-path %t/submodules.swiftmodule -module-cache-path %t/clang-module-cache %s
// RUN: echo 'import submodules; println("\(x), \(y)")' | %swift %clang-importer-sdk -parse - -I %t
// RUN: echo 'import submodules; println("\(x), \(y)")' | not %swift -parse - -I %t 2>&1 | FileCheck -check-prefix=MISSING %s
import ctypes
import ctypes.bits
// MISSING: missing required modules:
// MISSING-DAG: 'ctypes.bits'
// MISSING-DAG: 'ctypes'
// From bits submodule
var x : DWORD = MY_INT
var y : CInt = x
public var x : DWORD = MY_INT
public var y : CInt = x