Skip to content

Commit e248f82

Browse files
committed
Add support for loading cross-import files
1 parent 6cce3d3 commit e248f82

33 files changed

+467
-10
lines changed

include/swift/AST/DiagnosticsCommon.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,16 @@ WARNING(warn_property_wrapper_module_scope,none,
165165
"wrapper %0; please qualify the reference with %1",
166166
(DeclNameRef, Identifier))
167167

168+
//------------------------------------------------------------------------------
169+
// MARK: Cross-import overlay loading diagnostics
170+
//------------------------------------------------------------------------------
171+
ERROR(cannot_load_swiftoverlay_file, none,
172+
"cannot load cross-import overlay for %0 and %1: %2 (declared by '%3')",
173+
(Identifier, Identifier, StringRef, StringRef))
174+
ERROR(cannot_list_swiftcrossimport_dir, none,
175+
"cannot list cross-import overlays for %0: %1 (declared in '%2')",
176+
(Identifier, StringRef, StringRef))
177+
168178
#ifndef DIAG_NO_UNDEF
169179
# if defined(DIAG)
170180
# undef DIAG

include/swift/AST/FileUnit.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ class FileUnit : public DeclContext {
214214
virtual void
215215
collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const {}
216216

217+
/// Returns the path of the file or directory that defines the module
218+
/// represented by this \c FileUnit, or empty string if there is none.
219+
/// Cross-import overlay specifiers are found relative to this path.
220+
virtual StringRef getModuleDefiningPath() const {
221+
return "";
222+
}
223+
217224
/// True if this file contains the main class for the module.
218225
bool hasMainClass() const {
219226
return getMainClass();

include/swift/AST/Module.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ enum class ResilienceStrategy : unsigned {
124124
Resilient
125125
};
126126

127+
class OverlayFile;
128+
127129
/// The minimum unit of compilation.
128130
///
129131
/// A module is made up of several file-units, which are all part of the same
@@ -203,6 +205,10 @@ class ModuleDecl : public DeclContext, public TypeDecl {
203205

204206
SmallVector<FileUnit *, 2> Files;
205207

208+
friend OverlayFile;
209+
llvm::SmallDenseMap<Identifier, SmallVector<OverlayFile *, 1>>
210+
declaredCrossImports;
211+
206212
std::unique_ptr<SourceLookupCache> Cache;
207213
SourceLookupCache &getSourceLookupCache() const;
208214

@@ -252,6 +258,22 @@ class ModuleDecl : public DeclContext, public TypeDecl {
252258
void addFile(FileUnit &newFile);
253259
void removeFile(FileUnit &existingFile);
254260

261+
/// Add a file declaring a cross-import overlay.
262+
void addCrossImportOverlayFile(StringRef file);
263+
264+
/// Append to \p overlayNames the names of all modules that this module
265+
/// declares should be imported when \p bystanderName is imported.
266+
///
267+
/// This operation is asymmetric: you will get different results if you
268+
/// reverse the positions of the two modules involved in the cross-import.
269+
void findDeclaredCrossImportOverlays(
270+
Identifier bystanderName, SmallVectorImpl<Identifier> &overlayNames,
271+
SourceLoc diagLoc);
272+
273+
/// Get the list of all modules this module declares a cross-import with.
274+
void getDeclaredCrossImportBystanders(
275+
SmallVectorImpl<Identifier> &bystanderNames);
276+
255277
/// Convenience accessor for clients that know what kind of file they're
256278
/// dealing with.
257279
SourceFile &getMainSourceFile(SourceFileKind expectedKind) const;

include/swift/AST/ModuleLoader.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ namespace swift {
3838
class AbstractFunctionDecl;
3939
class ClangImporterOptions;
4040
class ClassDecl;
41+
class FileUnit;
4142
class ModuleDecl;
4243
class NominalTypeDecl;
4344
class TypeDecl;
@@ -154,6 +155,10 @@ class ModuleLoader {
154155

155156
/// Verify all modules loaded by this loader.
156157
virtual void verifyAllModules() { }
158+
159+
/// Discover overlays declared alongside this file and add infomation about
160+
/// them to it.
161+
void findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module, FileUnit *file);
157162
};
158163

159164
} // namespace swift

include/swift/ClangImporter/ClangModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ class ClangModuleUnit final : public LoadedFile {
118118
Optional<clang::ExternalASTSource::ASTSourceDescriptor>
119119
getASTSourceDescriptor() const;
120120

121+
virtual StringRef getModuleDefiningPath() const override;
122+
121123
static bool classof(const FileUnit *file) {
122124
return file->getKind() == FileUnitKind::ClangModule;
123125
}

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,8 @@ class SerializedASTFile final : public LoadedFile {
393393

394394
virtual StringRef getFilename() const override;
395395

396+
virtual StringRef getModuleDefiningPath() const override;
397+
396398
ClassDecl *getMainClass() const override;
397399

398400
bool hasEntryPoint() const override;

lib/AST/Module.cpp

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,189 @@ const clang::Module *ModuleDecl::findUnderlyingClangModule() const {
14541454
return nullptr;
14551455
}
14561456

1457+
//===----------------------------------------------------------------------===//
1458+
// Cross-Import Overlays
1459+
//===----------------------------------------------------------------------===//
1460+
1461+
namespace swift {
1462+
/// Represents a file containing information about cross-module overlays.
1463+
class OverlayFile {
1464+
/// If not empty, contains the name of the cross-import file that must be
1465+
/// loaded; \c overlayModuleNames is not valid and must be empty.
1466+
///
1467+
/// If empty, \c overlayModuleNames is valid and will contain the list of
1468+
/// module names unless loading the file failed.
1469+
StringRef filePath;
1470+
1471+
/// The list of module names; empty if loading failed.
1472+
llvm::TinyPtrVector<Identifier> overlayModuleNames;
1473+
1474+
/// Actually loads the overlay module name list. This should mutate
1475+
/// \c overlayModuleNames, but not \c filePath.
1476+
void loadOverlayModuleNames(ModuleDecl *M, SourceLoc diagLoc,
1477+
Identifier bystandingModule);
1478+
1479+
public:
1480+
// Only allocate in ASTContexts.
1481+
void *operator new(size_t bytes) = delete;
1482+
void *operator new(size_t bytes, const ASTContext &ctx,
1483+
unsigned alignment = alignof(OverlayFile)) {
1484+
return ctx.Allocate(bytes, alignment);
1485+
}
1486+
1487+
OverlayFile(StringRef filePath)
1488+
: filePath(filePath), overlayModuleNames() { }
1489+
1490+
/// Returns the list of additional modules that should be imported if both
1491+
/// the primary and secondary modules have been imported. This may load a
1492+
/// file; if so, it will diagnose any errors itself and arrange for the file
1493+
/// to not be loaded again.
1494+
///
1495+
/// The result can be empty, either because of an error or because the file
1496+
/// didn't contain any overlay module names.
1497+
ArrayRef<Identifier> getOverlayModuleNames(ModuleDecl *M, SourceLoc diagLoc,
1498+
Identifier bystandingModule) {
1499+
if (!filePath.empty()) {
1500+
assert(overlayModuleNames.empty()
1501+
&& "cross-import list can't be full before loading");
1502+
loadOverlayModuleNames(M, diagLoc, bystandingModule);
1503+
filePath = "";
1504+
}
1505+
return overlayModuleNames;
1506+
}
1507+
};
1508+
}
1509+
1510+
void ModuleDecl::addCrossImportOverlayFile(StringRef file) {
1511+
auto &ctx = getASTContext();
1512+
1513+
Identifier secondaryModule = ctx.getIdentifier(llvm::sys::path::stem(file));
1514+
declaredCrossImports[secondaryModule]
1515+
.push_back(new (ctx) OverlayFile(ctx.AllocateCopy(file)));
1516+
}
1517+
1518+
void ModuleDecl::
1519+
findDeclaredCrossImportOverlays(Identifier bystanderName,
1520+
SmallVectorImpl<Identifier> &overlayNames,
1521+
SourceLoc diagLoc) {
1522+
if (getName() == bystanderName)
1523+
// We don't currently support self-cross-imports.
1524+
return;
1525+
1526+
for (auto &crossImportFile : declaredCrossImports[bystanderName])
1527+
llvm::copy(crossImportFile->getOverlayModuleNames(this, diagLoc,
1528+
bystanderName),
1529+
std::back_inserter(overlayNames));
1530+
}
1531+
1532+
void ModuleDecl::getDeclaredCrossImportBystanders(
1533+
SmallVectorImpl<Identifier> &otherModules) {
1534+
for (auto &pair : declaredCrossImports)
1535+
otherModules.push_back(std::get<0>(pair));
1536+
}
1537+
1538+
namespace {
1539+
struct OverlayFileContents {
1540+
struct Module {
1541+
std::string name;
1542+
};
1543+
1544+
unsigned version;
1545+
std::vector<Module> modules;
1546+
1547+
static llvm::ErrorOr<OverlayFileContents>
1548+
load(std::unique_ptr<llvm::MemoryBuffer> input,
1549+
SmallVectorImpl<std::string> &errorMessages);
1550+
};
1551+
} // end anonymous namespace
1552+
1553+
namespace llvm {
1554+
namespace yaml {
1555+
template <>
1556+
struct MappingTraits<OverlayFileContents::Module> {
1557+
static void mapping(IO &io, OverlayFileContents::Module &module) {
1558+
io.mapRequired("name", module.name);
1559+
}
1560+
};
1561+
1562+
template <>
1563+
struct SequenceElementTraits<OverlayFileContents::Module> {
1564+
static const bool flow = false;
1565+
};
1566+
1567+
template <>
1568+
struct MappingTraits<OverlayFileContents> {
1569+
static void mapping(IO &io, OverlayFileContents &contents) {
1570+
io.mapRequired("version", contents.version);
1571+
io.mapRequired("modules", contents.modules);
1572+
}
1573+
};
1574+
}
1575+
} // end namespace 'llvm'
1576+
1577+
static void pushYAMLError(const llvm::SMDiagnostic &diag, void *Context) {
1578+
auto &errorMessages = *static_cast<SmallVectorImpl<std::string> *>(Context);
1579+
errorMessages.emplace_back(diag.getMessage());
1580+
}
1581+
1582+
llvm::ErrorOr<OverlayFileContents>
1583+
OverlayFileContents::load(std::unique_ptr<llvm::MemoryBuffer> input,
1584+
SmallVectorImpl<std::string> &errorMessages) {
1585+
llvm::yaml::Input yamlInput(input->getBuffer(), /*Ctxt=*/nullptr,
1586+
pushYAMLError, &errorMessages);
1587+
OverlayFileContents contents;
1588+
yamlInput >> contents;
1589+
1590+
if (auto error = yamlInput.error())
1591+
return error;
1592+
1593+
if (contents.version > 1) {
1594+
auto message = Twine("key 'version' has invalid value: ") + Twine(contents.version);
1595+
errorMessages.push_back(message.str());
1596+
return make_error_code(std::errc::result_out_of_range);
1597+
}
1598+
1599+
return contents;
1600+
}
1601+
1602+
void OverlayFile::loadOverlayModuleNames(ModuleDecl *M, SourceLoc diagLoc,
1603+
Identifier bystanderName) {
1604+
assert(!filePath.empty());
1605+
1606+
auto &ctx = M->getASTContext();
1607+
llvm::vfs::FileSystem &fs = *ctx.SourceMgr.getFileSystem();
1608+
1609+
auto bufOrError = fs.getBufferForFile(filePath);
1610+
if (!bufOrError) {
1611+
M->getASTContext().Diags
1612+
.diagnose(diagLoc, diag::cannot_load_swiftoverlay_file,
1613+
M->getName(), bystanderName, bufOrError.getError().message(),
1614+
filePath);
1615+
return;
1616+
}
1617+
1618+
SmallVector<std::string, 4> errorMessages;
1619+
auto contentsOrErr = OverlayFileContents::load(std::move(bufOrError.get()),
1620+
errorMessages);
1621+
if (!contentsOrErr) {
1622+
if (errorMessages.empty())
1623+
errorMessages.push_back(contentsOrErr.getError().message());
1624+
1625+
for (auto message : errorMessages)
1626+
M->getASTContext().Diags
1627+
.diagnose(diagLoc, diag::cannot_load_swiftoverlay_file,
1628+
M->getName(), bystanderName, message, filePath);
1629+
return;
1630+
}
1631+
1632+
auto contents = std::move(*contentsOrErr);
1633+
1634+
for (const auto &module : contents.modules) {
1635+
auto moduleIdent = ctx.getIdentifier(module.name);
1636+
overlayModuleNames.push_back(moduleIdent);
1637+
}
1638+
}
1639+
14571640
//===----------------------------------------------------------------------===//
14581641
// SourceFile Implementation
14591642
//===----------------------------------------------------------------------===//

lib/AST/ModuleLoader.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17+
#include "swift/AST/ASTContext.h"
18+
#include "swift/AST/DiagnosticsCommon.h"
19+
#include "swift/AST/FileUnit.h"
1720
#include "swift/AST/ModuleLoader.h"
21+
#include "swift/Basic/FileTypes.h"
22+
#include "swift/Basic/Platform.h"
1823
#include "clang/Frontend/Utils.h"
1924
#include "swift/ClangImporter/ClangImporter.h"
2025

@@ -54,4 +59,73 @@ DependencyTracker::getClangCollector() {
5459
return clangCollector;
5560
}
5661

62+
static bool findOverlayFilesInDirectory(SourceLoc diagLoc, StringRef path,
63+
ModuleDecl *module,
64+
DependencyTracker * const tracker) {
65+
using namespace llvm::sys;
66+
using namespace file_types;
67+
68+
ASTContext &ctx = module->getASTContext();
69+
auto fs = ctx.SourceMgr.getFileSystem();
70+
71+
std::error_code error;
72+
for (auto dir = fs->dir_begin(path, error);
73+
!error && dir != llvm::vfs::directory_iterator();
74+
dir.increment(error)) {
75+
StringRef file = dir->path();
76+
if (lookupTypeForExtension(path::extension(file)) != TY_SwiftOverlayFile)
77+
continue;
78+
79+
module->addCrossImportOverlayFile(file);
80+
81+
// FIXME: Better to add it only if we load it.
82+
if (tracker)
83+
tracker->addDependency(file, module->isSystemModule());
84+
}
85+
86+
if (error && error != std::errc::no_such_file_or_directory) {
87+
ctx.Diags.diagnose(diagLoc, diag::cannot_list_swiftcrossimport_dir,
88+
module->getName(), error.message(), path);
89+
}
90+
return !error;
91+
}
92+
93+
void ModuleLoader::findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module,
94+
FileUnit *file) {
95+
using namespace llvm::sys;
96+
using namespace file_types;
97+
98+
auto &langOpts = module->getASTContext().LangOpts;
99+
100+
SmallString<64> dirPath{file->getModuleDefiningPath()};
101+
102+
if (dirPath.empty())
103+
return;
104+
path::remove_filename(dirPath);
105+
106+
// <parent-dir>/<module-name>.swiftcrossimport
107+
path::append(dirPath, file->getExportedModuleName());
108+
path::replace_extension(dirPath, getExtension(TY_SwiftCrossImportDir));
109+
if (!findOverlayFilesInDirectory(diagLoc, dirPath, module, dependencyTracker))
110+
// If we diagnosed an error, or we didn't find the directory at all, don't
111+
// bother trying the target-specific directories.
112+
return;
113+
114+
// <parent-dir>/<module-name>.swiftcrossimport/<target-module-triple>
115+
auto moduleTriple = getTargetSpecificModuleTriple(langOpts.Target);
116+
path::append(dirPath, moduleTriple.str());
117+
findOverlayFilesInDirectory(diagLoc, dirPath, module, dependencyTracker);
118+
119+
if (!langOpts.TargetVariant)
120+
return;
121+
122+
path::remove_filename(dirPath);
123+
124+
// <parent-dir>/<module-name>.swiftcrossimport/<targetvariant-module-triple>
125+
auto moduleVariantTriple =
126+
getTargetSpecificModuleTriple(*langOpts.TargetVariant);
127+
path::append(dirPath, moduleVariantTriple.str());
128+
findOverlayFilesInDirectory(diagLoc, dirPath, module, dependencyTracker);
129+
}
130+
57131
} // namespace swift

0 commit comments

Comments
 (0)