DependenciesScanner: teach the scanner to handle cross-import overlays

This commit is contained in:
Xi Ge
2020-06-08 12:29:12 -07:00
parent b996a21d08
commit 42cc989136
9 changed files with 164 additions and 33 deletions

View File

@@ -159,11 +159,11 @@ NOTE(circular_type_resolution_note,none,
// MARK: Cross-import overlay loading diagnostics // MARK: Cross-import overlay loading diagnostics
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
ERROR(cannot_load_swiftoverlay_file, none, ERROR(cannot_load_swiftoverlay_file, none,
"cannot load cross-import overlay for %0 and %1: %2 (declared by '%3')", "cannot load cross-import overlay for '%0' and '%1': %2 (declared by '%3')",
(Identifier, Identifier, StringRef, StringRef)) (StringRef, StringRef, StringRef, StringRef))
ERROR(cannot_list_swiftcrossimport_dir, none, ERROR(cannot_list_swiftcrossimport_dir, none,
"cannot list cross-import overlays for %0: %1 (declared in '%2')", "cannot list cross-import overlays for '%0': %1 (declared in '%2')",
(Identifier, StringRef, StringRef)) (StringRef, StringRef, StringRef))
WARNING(cross_imported_by_both_modules, none, WARNING(cross_imported_by_both_modules, none,
"modules %0 and %1 both declare module %2 as a cross-import overlay, " "modules %0 and %1 both declare module %2 as a cross-import overlay, "
"which may cause paradoxical behavior when looking up names in them; " "which may cause paradoxical behavior when looking up names in them; "

View File

@@ -391,6 +391,11 @@ public:
/// Add a file declaring a cross-import overlay. /// Add a file declaring a cross-import overlay.
void addCrossImportOverlayFile(StringRef file); void addCrossImportOverlayFile(StringRef file);
/// Collect cross-import overlay names from a given YAML file path.
static llvm::SmallSetVector<Identifier, 4>
collectCrossImportOverlay(ASTContext &ctx, StringRef file,
StringRef moduleName, StringRef& bystandingModule);
/// If this method returns \c false, the module does not declare any /// If this method returns \c false, the module does not declare any
/// cross-import overlays. /// cross-import overlays.
/// ///

View File

@@ -29,6 +29,8 @@ namespace swift {
class ClangModuleDependenciesCacheImpl; class ClangModuleDependenciesCacheImpl;
class SourceFile; class SourceFile;
class ASTContext;
class Identifier;
/// Which kind of module dependencies we are looking for. /// Which kind of module dependencies we are looking for.
enum class ModuleDependenciesKind : int8_t { enum class ModuleDependenciesKind : int8_t {
@@ -246,6 +248,11 @@ public:
/// Add (Clang) module on which the bridging header depends. /// Add (Clang) module on which the bridging header depends.
void addBridgingModuleDependency(StringRef module, void addBridgingModuleDependency(StringRef module,
llvm::StringSet<> &alreadyAddedModules); llvm::StringSet<> &alreadyAddedModules);
/// Collect a map from a secondary module name to a list of cross-import
/// overlays, when this current module serves as the primary module.
llvm::StringMap<llvm::SmallSetVector<Identifier, 4>>
collectCrossImportOverlayNames(ASTContext &ctx, StringRef moduleName);
}; };
using ModuleDependencyID = std::pair<std::string, ModuleDependenciesKind>; using ModuleDependencyID = std::pair<std::string, ModuleDependenciesKind>;

View File

@@ -1520,6 +1520,7 @@ const clang::Module *ModuleDecl::findUnderlyingClangModule() const {
namespace swift { namespace swift {
/// Represents a file containing information about cross-module overlays. /// Represents a file containing information about cross-module overlays.
class OverlayFile { class OverlayFile {
friend class ModuleDecl;
/// The file that data should be loaded from. /// The file that data should be loaded from.
StringRef filePath; StringRef filePath;
@@ -1536,7 +1537,10 @@ class OverlayFile {
/// before returning. /// before returning.
bool loadOverlayModuleNames(const ModuleDecl *M, SourceLoc diagLoc, bool loadOverlayModuleNames(const ModuleDecl *M, SourceLoc diagLoc,
Identifier bystandingModule); Identifier bystandingModule);
bool loadOverlayModuleNames(ASTContext &ctx,
StringRef module,
StringRef bystandingModule,
SourceLoc diagLoc);
public: public:
// Only allocate in ASTContexts. // Only allocate in ASTContexts.
void *operator new(size_t bytes) = delete; void *operator new(size_t bytes) = delete;
@@ -1577,6 +1581,21 @@ void ModuleDecl::addCrossImportOverlayFile(StringRef file) {
.push_back(new (ctx) OverlayFile(ctx.AllocateCopy(file))); .push_back(new (ctx) OverlayFile(ctx.AllocateCopy(file)));
} }
llvm::SmallSetVector<Identifier, 4>
ModuleDecl::collectCrossImportOverlay(ASTContext &ctx,
StringRef file,
StringRef moduleName,
StringRef &bystandingModule) {
OverlayFile ovFile(file);
bystandingModule = llvm::sys::path::stem(file);
ovFile.loadOverlayModuleNames(ctx, moduleName, bystandingModule, SourceLoc());
llvm::SmallSetVector<Identifier, 4> result;
for (auto Id: ovFile.overlayModuleNames) {
result.insert(Id);
}
return result;
}
bool ModuleDecl::mightDeclareCrossImportOverlays() const { bool ModuleDecl::mightDeclareCrossImportOverlays() const {
return !declaredCrossImports.empty(); return !declaredCrossImports.empty();
} }
@@ -1803,15 +1822,15 @@ OverlayFileContents::load(std::unique_ptr<llvm::MemoryBuffer> input,
} }
bool bool
OverlayFile::loadOverlayModuleNames(const ModuleDecl *M, SourceLoc diagLoc, OverlayFile::loadOverlayModuleNames(ASTContext &ctx, StringRef module,
Identifier bystanderName) { StringRef bystanderName,
auto &ctx = M->getASTContext(); SourceLoc diagLoc) {
llvm::vfs::FileSystem &fs = *ctx.SourceMgr.getFileSystem(); llvm::vfs::FileSystem &fs = *ctx.SourceMgr.getFileSystem();
auto bufOrError = fs.getBufferForFile(filePath); auto bufOrError = fs.getBufferForFile(filePath);
if (!bufOrError) { if (!bufOrError) {
ctx.Diags.diagnose(diagLoc, diag::cannot_load_swiftoverlay_file, ctx.Diags.diagnose(diagLoc, diag::cannot_load_swiftoverlay_file,
M->getName(), bystanderName, module, bystanderName,
bufOrError.getError().message(), filePath); bufOrError.getError().message(), filePath);
return false; return false;
} }
@@ -1825,7 +1844,7 @@ OverlayFile::loadOverlayModuleNames(const ModuleDecl *M, SourceLoc diagLoc,
for (auto message : errorMessages) for (auto message : errorMessages)
ctx.Diags.diagnose(diagLoc, diag::cannot_load_swiftoverlay_file, ctx.Diags.diagnose(diagLoc, diag::cannot_load_swiftoverlay_file,
M->getName(), bystanderName, message, filePath); module, bystanderName, message, filePath);
return false; return false;
} }
@@ -1839,6 +1858,15 @@ OverlayFile::loadOverlayModuleNames(const ModuleDecl *M, SourceLoc diagLoc,
return true; return true;
} }
bool
OverlayFile::loadOverlayModuleNames(const ModuleDecl *M, SourceLoc diagLoc,
Identifier bystanderName) {
return loadOverlayModuleNames(M->getASTContext(),
M->getName().str(),
bystanderName.str(),
diagLoc);
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// SourceFile Implementation // SourceFile Implementation
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@@ -59,13 +59,13 @@ DependencyTracker::getClangCollector() {
return clangCollector; return clangCollector;
} }
static bool findOverlayFilesInDirectory(SourceLoc diagLoc, StringRef path, static bool findOverlayFilesInDirectory(ASTContext &ctx, StringRef path,
ModuleDecl *module, StringRef moduleName,
DependencyTracker * const tracker) { SourceLoc diagLoc,
llvm::function_ref<void(StringRef)> callback) {
using namespace llvm::sys; using namespace llvm::sys;
using namespace file_types; using namespace file_types;
ASTContext &ctx = module->getASTContext();
auto fs = ctx.SourceMgr.getFileSystem(); auto fs = ctx.SourceMgr.getFileSystem();
std::error_code error; std::error_code error;
@@ -76,27 +76,23 @@ static bool findOverlayFilesInDirectory(SourceLoc diagLoc, StringRef path,
if (lookupTypeForExtension(path::extension(file)) != TY_SwiftOverlayFile) if (lookupTypeForExtension(path::extension(file)) != TY_SwiftOverlayFile)
continue; continue;
module->addCrossImportOverlayFile(file); callback(file);
// FIXME: Better to add it only if we load it.
if (tracker)
tracker->addDependency(file, module->isSystemModule());
} }
if (error && error != std::errc::no_such_file_or_directory) { if (error && error != std::errc::no_such_file_or_directory) {
ctx.Diags.diagnose(diagLoc, diag::cannot_list_swiftcrossimport_dir, ctx.Diags.diagnose(diagLoc, diag::cannot_list_swiftcrossimport_dir,
module->getName(), error.message(), path); moduleName, error.message(), path);
} }
return !error; return !error;
} }
void ModuleLoader::findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module, static void findOverlayFilesInternal(ASTContext &ctx, StringRef moduleDefiningPath,
FileUnit *file) { StringRef moduleName,
SourceLoc diagLoc,
llvm::function_ref<void(StringRef)> callback) {
using namespace llvm::sys; using namespace llvm::sys;
using namespace file_types; using namespace file_types;
auto &langOpts = ctx.LangOpts;
auto &langOpts = module->getASTContext().LangOpts;
// This method constructs several paths to directories near the module and // This method constructs several paths to directories near the module and
// scans them for .swiftoverlay files. These paths can be in various // scans them for .swiftoverlay files. These paths can be in various
// directories and have a few different filenames at the end, but I'll // directories and have a few different filenames at the end, but I'll
@@ -106,19 +102,17 @@ void ModuleLoader::findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module,
// /usr/lib/swift/FooKit.swiftmodule/x86_64-apple-macos.swiftinterface // /usr/lib/swift/FooKit.swiftmodule/x86_64-apple-macos.swiftinterface
// dirPath = /usr/lib/swift/FooKit.swiftmodule // dirPath = /usr/lib/swift/FooKit.swiftmodule
SmallString<64> dirPath{file->getModuleDefiningPath()}; SmallString<64> dirPath{moduleDefiningPath};
if (dirPath.empty())
return;
// dirPath = /usr/lib/swift/ // dirPath = /usr/lib/swift/
path::remove_filename(dirPath); path::remove_filename(dirPath);
// dirPath = /usr/lib/swift/FooKit.swiftcrossimport // dirPath = /usr/lib/swift/FooKit.swiftcrossimport
path::append(dirPath, file->getExportedModuleName()); path::append(dirPath, moduleName);
path::replace_extension(dirPath, getExtension(TY_SwiftCrossImportDir)); path::replace_extension(dirPath, getExtension(TY_SwiftCrossImportDir));
// Search for swiftoverlays that apply to all platforms. // Search for swiftoverlays that apply to all platforms.
if (!findOverlayFilesInDirectory(diagLoc, dirPath, module, dependencyTracker)) if (!findOverlayFilesInDirectory(ctx, dirPath, moduleName, diagLoc, callback))
// If we diagnosed an error, or we didn't find the directory at all, don't // If we diagnosed an error, or we didn't find the directory at all, don't
// bother trying the target-specific directories. // bother trying the target-specific directories.
return; return;
@@ -128,7 +122,7 @@ void ModuleLoader::findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module,
path::append(dirPath, moduleTriple.str()); path::append(dirPath, moduleTriple.str());
// Search for swiftoverlays specific to the target triple's platform. // Search for swiftoverlays specific to the target triple's platform.
findOverlayFilesInDirectory(diagLoc, dirPath, module, dependencyTracker); findOverlayFilesInDirectory(ctx, dirPath, moduleName, diagLoc, callback);
// The rest of this handles target variant triples, which are only used for // The rest of this handles target variant triples, which are only used for
// certain MacCatalyst builds. // certain MacCatalyst builds.
@@ -142,7 +136,59 @@ void ModuleLoader::findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module,
path::append(dirPath, moduleVariantTriple.str()); path::append(dirPath, moduleVariantTriple.str());
// Search for swiftoverlays specific to the target variant's platform. // Search for swiftoverlays specific to the target variant's platform.
findOverlayFilesInDirectory(diagLoc, dirPath, module, dependencyTracker); findOverlayFilesInDirectory(ctx, dirPath, moduleName, diagLoc, callback);
} }
void ModuleLoader::findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module,
FileUnit *file) {
using namespace llvm::sys;
using namespace file_types;
if (file->getModuleDefiningPath().empty())
return;
findOverlayFilesInternal(module->getASTContext(),
file->getModuleDefiningPath(),
module->getName().str(),
diagLoc,
[&](StringRef file) {
module->addCrossImportOverlayFile(file);
// FIXME: Better to add it only if we load it.
if (dependencyTracker)
dependencyTracker->addDependency(file, module->isSystemModule());
});
}
llvm::StringMap<llvm::SmallSetVector<Identifier, 4>>
ModuleDependencies::collectCrossImportOverlayNames(ASTContext &ctx,
StringRef moduleName) {
using namespace llvm::sys;
using namespace file_types;
Optional<std::string> modulePath;
// Mimic getModuleDefiningPath() for Swift and Clang module.
if (auto *swiftDep = dyn_cast<SwiftModuleDependenciesStorage>(storage.get())) {
// Prefer interface path to binary module path if we have it.
modulePath = swiftDep->swiftInterfaceFile;
if (!modulePath.hasValue())
modulePath = swiftDep->compiledModulePath;
assert(modulePath.hasValue());
StringRef parentDir = llvm::sys::path::parent_path(*modulePath);
if (llvm::sys::path::extension(parentDir) == ".swiftmodule") {
modulePath = parentDir.str();
}
} else {
modulePath = cast<ClangModuleDependenciesStorage>(storage.get())->moduleMapFile;
assert(modulePath.hasValue());
}
// A map from secondary module name to a vector of overlay names.
llvm::StringMap<llvm::SmallSetVector<Identifier, 4>> result;
findOverlayFilesInternal(ctx, *modulePath, moduleName, SourceLoc(),
[&](StringRef file) {
StringRef bystandingModule;
auto overlayNames =
ModuleDecl::collectCrossImportOverlay(ctx, file, moduleName,
bystandingModule);
result[bystandingModule] = std::move(overlayNames);
});
return result;
}
} // namespace swift } // namespace swift

View File

@@ -138,7 +138,38 @@ static std::vector<ModuleDependencyID> resolveDirectDependencies(
} }
} }
} }
// Only resolve cross-import overlays when this is the main module.
// For other modules, these overlays are explicitly written.
bool isMainModule =
instance.getMainModule()->getName().str() == module.first &&
module.second == ModuleDependenciesKind::Swift;
if (isMainModule) {
// Modules explicitly imported. Only these can be secondary module.
std::vector<ModuleDependencyID> explicitImports = result;
for (unsigned I = 0; I != result.size(); ++I) {
auto dep = result[I];
auto moduleName = dep.first;
auto dependencies = *cache.findDependencies(moduleName, dep.second);
// Collect a map from secondary module name to cross-import overlay names.
auto overlayMap = dependencies.collectCrossImportOverlayNames(
instance.getASTContext(), moduleName);
if (overlayMap.empty())
continue;
std::for_each(explicitImports.begin(), explicitImports.end(),
[&](ModuleDependencyID Id) {
// check if any explicitly imported modules can serve as a secondary
// module, and add the overlay names to the dependencies list.
for (auto overlayName: overlayMap[Id.first]) {
if (auto found = ctx.getModuleDependencies(overlayName.str(),
/*onlyClangModule=*/false,
cache,
ASTDelegate)) {
result.push_back({overlayName.str(), found->getKind()});
}
}
});
}
}
return result; return result;
} }

View File

@@ -0,0 +1,5 @@
%YAML 1.2
---
version: 1
modules:
- name: _cross_import_E

View File

@@ -0,0 +1,6 @@
// swift-interface-format-version: 1.0
// swift-module-flags: -module-name _cross_import_E
import Swift
import E
import SubE
public func funcCrossImportE() {}

View File

@@ -80,6 +80,9 @@ import SubE
// CHECK-NEXT: { // CHECK-NEXT: {
// CHECK-NEXT: "swift": "A" // CHECK-NEXT: "swift": "A"
// CHECK-NEXT: } // CHECK-NEXT: }
// CHECK-NEXT: {
// CHECK-NEXT: "swift": "_cross_import_E"
// CHECK-NEXT: }
// CHECK: "bridgingHeader": // CHECK: "bridgingHeader":
// CHECK-NEXT: "path": // CHECK-NEXT: "path":