Skip to content

Commit bce2345

Browse files
[ScanDependencies] Get accurate macro dependency
Build an accurate macro dependency for swift caching. Specifically, do not include not used macro plugins into the dependency, which might cause false negatives for cache hits. This also builds the foundation for future improvement when dependency scanning will determine the macro plugin to load and swift-frontend do not need to redo the work. rdar://127116512
1 parent 1c1b6f7 commit bce2345

File tree

5 files changed

+111
-86
lines changed

5 files changed

+111
-86
lines changed

include/swift/AST/ModuleDependencies.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ enum class ModuleDependencyKind : int8_t {
9090
LastKind = SwiftPlaceholder + 1
9191
};
9292

93+
/// This is used to idenfity a specific macro plugin dependency.
94+
struct MacroPluginDependency {
95+
std::string LibraryPath;
96+
std::string ExecutablePath;
97+
};
98+
9399
/// This is used to identify a specific module.
94100
struct ModuleDependencyID {
95101
std::string ModuleName;
@@ -246,6 +252,9 @@ struct CommonSwiftTextualModuleDependencyDetails {
246252
/// (Clang) modules on which the bridging header depends.
247253
std::vector<std::string> bridgingModuleDependencies;
248254

255+
/// The macro dependencies.
256+
llvm::StringMap<MacroPluginDependency> macroDependencies;
257+
249258
/// The Swift frontend invocation arguments to build the Swift module from the
250259
/// interface.
251260
std::vector<std::string> buildCommandLine;
@@ -311,6 +320,12 @@ class SwiftInterfaceModuleDependenciesStorage
311320
void updateCommandLine(const std::vector<std::string> &newCommandLine) {
312321
textualModuleDetails.buildCommandLine = newCommandLine;
313322
}
323+
324+
void addMacroDependency(StringRef module, StringRef libraryPath,
325+
StringRef executablePath) {
326+
textualModuleDetails.macroDependencies.insert(
327+
{module, {libraryPath.str(), executablePath.str()}});
328+
}
314329
};
315330

316331
/// Describes the dependencies of a Swift module
@@ -361,6 +376,12 @@ class SwiftSourceModuleDependenciesStorage
361376
void addTestableImport(ImportPath::Module module) {
362377
testableImports.insert(module.front().Item.str());
363378
}
379+
380+
void addMacroDependency(StringRef module, StringRef libraryPath,
381+
StringRef executablePath) {
382+
textualModuleDetails.macroDependencies.insert(
383+
{module, {libraryPath.str(), executablePath.str()}});
384+
}
364385
};
365386

366387
/// Describes the dependencies of a pre-built Swift module (with no
@@ -759,6 +780,10 @@ class ModuleDependencyInfo {
759780
/// For a Source dependency, register a `Testable` import
760781
void addTestableImport(ImportPath::Module module);
761782

783+
/// For a Source dependency, register a macro dependency.
784+
void addMacroDependency(StringRef module, StringRef libraryPath,
785+
StringRef executablePath);
786+
762787
/// Whether or not a queried module name is a `@Testable` import dependency
763788
/// of this module. Can only return `true` for Swift source modules.
764789
bool isTestableImport(StringRef moduleName) const;

lib/AST/ModuleDependencies.cpp

Lines changed: 57 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
#include "swift/AST/Decl.h"
1818
#include "swift/AST/DiagnosticsFrontend.h"
1919
#include "swift/AST/DiagnosticsSema.h"
20+
#include "swift/AST/Evaluator.h"
21+
#include "swift/AST/MacroDefinition.h"
22+
#include "swift/AST/PluginLoader.h"
2023
#include "swift/AST/SourceFile.h"
21-
#include "swift/Basic/Assertions.h"
24+
#include "swift/AST/TypeCheckRequests.h"
2225
#include "swift/Frontend/Frontend.h"
2326
#include "llvm/CAS/CASProvidingFileSystem.h"
2427
#include "llvm/CAS/CachingOnDiskFileSystem.h"
@@ -104,6 +107,20 @@ void ModuleDependencyInfo::addTestableImport(ImportPath::Module module) {
104107
dyn_cast<SwiftSourceModuleDependenciesStorage>(storage.get())->addTestableImport(module);
105108
}
106109

110+
void ModuleDependencyInfo::addMacroDependency(StringRef module,
111+
StringRef libraryPath,
112+
StringRef executablePath) {
113+
if (auto swiftSourceStorage =
114+
dyn_cast<SwiftSourceModuleDependenciesStorage>(storage.get()))
115+
swiftSourceStorage->addMacroDependency(module, libraryPath, executablePath);
116+
else if (auto swiftInterfaceStorage =
117+
dyn_cast<SwiftInterfaceModuleDependenciesStorage>(storage.get()))
118+
swiftInterfaceStorage->addMacroDependency(module, libraryPath,
119+
executablePath);
120+
else
121+
llvm_unreachable("Unexpected dependency kind");
122+
}
123+
107124
bool ModuleDependencyInfo::isTestableImport(StringRef moduleName) const {
108125
if (auto swiftSourceDepStorage = getAsSwiftSourceModule())
109126
return swiftSourceDepStorage->testableImports.contains(moduleName);
@@ -184,35 +201,45 @@ void ModuleDependencyInfo::addModuleImports(
184201
SmallVector<Decl *, 32> decls;
185202
sourceFile.getTopLevelDecls(decls);
186203
for (auto decl : decls) {
187-
auto importDecl = dyn_cast<ImportDecl>(decl);
188-
if (!importDecl)
189-
continue;
190-
191-
ImportPath::Builder scratch;
192-
auto realPath = importDecl->getRealModulePath(scratch);
193-
194-
// Explicit 'Builtin' import is not a part of the module's
195-
// dependency set, does not exist on the filesystem,
196-
// and is resolved within the compiler during compilation.
197-
SmallString<64> importedModuleName;
198-
realPath.getString(importedModuleName);
199-
if (importedModuleName == BUILTIN_NAME)
200-
continue;
201-
202-
// Ignore/diagnose tautological imports akin to import resolution
203-
if (!swift::dependencies::checkImportNotTautological(
204-
realPath, importDecl->getLoc(), sourceFile,
205-
importDecl->isExported()))
206-
continue;
207-
208-
addModuleImport(realPath, &alreadyAddedModules,
209-
sourceManager, importDecl->getLoc());
210-
211-
// Additionally, keep track of which dependencies of a Source
212-
// module are `@Testable`.
213-
if (getKind() == swift::ModuleDependencyKind::SwiftSource &&
214-
importDecl->isTestable())
215-
addTestableImport(realPath);
204+
if (auto importDecl = dyn_cast<ImportDecl>(decl)) {
205+
ImportPath::Builder scratch;
206+
auto realPath = importDecl->getRealModulePath(scratch);
207+
208+
// Explicit 'Builtin' import is not a part of the module's
209+
// dependency set, does not exist on the filesystem,
210+
// and is resolved within the compiler during compilation.
211+
SmallString<64> importedModuleName;
212+
realPath.getString(importedModuleName);
213+
if (importedModuleName == BUILTIN_NAME)
214+
continue;
215+
216+
// Ignore/diagnose tautological imports akin to import resolution
217+
if (!swift::dependencies::checkImportNotTautological(
218+
realPath, importDecl->getLoc(), sourceFile,
219+
importDecl->isExported()))
220+
continue;
221+
222+
addModuleImport(realPath, &alreadyAddedModules, sourceManager,
223+
importDecl->getLoc());
224+
225+
// Additionally, keep track of which dependencies of a Source
226+
// module are `@Testable`.
227+
if (getKind() == swift::ModuleDependencyKind::SwiftSource &&
228+
importDecl->isTestable())
229+
addTestableImport(realPath);
230+
} else if (auto macroDecl = dyn_cast<MacroDecl>(decl)) {
231+
auto macroDef = macroDecl->getDefinition();
232+
auto &ctx = macroDecl->getASTContext();
233+
if (macroDef.kind != MacroDefinition::Kind::External)
234+
continue;
235+
auto external = macroDef.getExternalMacro();
236+
PluginLoader &loader = ctx.getPluginLoader();
237+
auto &entry = loader.lookupPluginByModuleName(external.moduleName);
238+
if (entry.libraryPath.empty() && entry.executablePath.empty())
239+
continue;
240+
addMacroDependency(external.moduleName.str(), entry.libraryPath,
241+
entry.executablePath);
242+
}
216243
}
217244

218245
auto fileName = sourceFile.getFilename();
@@ -609,58 +636,6 @@ void SwiftDependencyTracker::addCommonSearchPathDeps(
609636
// Add VFSOverlay file.
610637
for (auto &Overlay: Opts.VFSOverlayFiles)
611638
FS->status(Overlay);
612-
613-
// Add plugin dylibs from the toolchain only by look through the plugin search
614-
// directory.
615-
auto recordFiles = [&](StringRef Path) {
616-
std::error_code EC;
617-
for (auto I = FS->dir_begin(Path, EC);
618-
!EC && I != llvm::vfs::directory_iterator(); I = I.increment(EC)) {
619-
if (I->type() != llvm::sys::fs::file_type::regular_file)
620-
continue;
621-
#if defined(_WIN32)
622-
constexpr StringRef libPrefix{};
623-
constexpr StringRef libSuffix = ".dll";
624-
#else
625-
constexpr StringRef libPrefix = "lib";
626-
constexpr StringRef libSuffix = LTDL_SHLIB_EXT;
627-
#endif
628-
StringRef filename = llvm::sys::path::filename(I->path());
629-
if (filename.starts_with(libPrefix) && filename.ends_with(libSuffix))
630-
FS->status(I->path());
631-
}
632-
};
633-
for (auto &entry : Opts.PluginSearchOpts) {
634-
switch (entry.getKind()) {
635-
636-
// '-load-plugin-library <library path>'.
637-
case PluginSearchOption::Kind::LoadPluginLibrary: {
638-
auto &val = entry.get<PluginSearchOption::LoadPluginLibrary>();
639-
FS->status(val.LibraryPath);
640-
break;
641-
}
642-
643-
// '-load-plugin-executable <executable path>#<module name>, ...'.
644-
case PluginSearchOption::Kind::LoadPluginExecutable: {
645-
// We don't have executable plugin in toolchain.
646-
break;
647-
}
648-
649-
// '-plugin-path <library search path>'.
650-
case PluginSearchOption::Kind::PluginPath: {
651-
auto &val = entry.get<PluginSearchOption::PluginPath>();
652-
recordFiles(val.SearchPath);
653-
break;
654-
}
655-
656-
// '-external-plugin-path <library search path>#<server path>'.
657-
case PluginSearchOption::Kind::ExternalPluginPath: {
658-
auto &val = entry.get<PluginSearchOption::ExternalPluginPath>();
659-
recordFiles(val.SearchPath);
660-
break;
661-
}
662-
}
663-
}
664639
}
665640

666641
void SwiftDependencyTracker::startTracking() {

lib/DependencyScan/ScanDependencies.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ static llvm::Error resolveExplicitModuleInputs(
244244
return E;
245245

246246
std::vector<std::string> commandLine = resolvingDepInfo.getCommandline();
247+
auto dependencyInfoCopy = resolvingDepInfo;
247248
for (const auto &depModuleID : dependencies) {
248249
const auto &depInfo = cache.findKnownDependency(depModuleID);
249250
switch (depModuleID.Kind) {
@@ -255,6 +256,14 @@ static llvm::Error resolveExplicitModuleInputs(
255256
: interfaceDepDetails->moduleCacheKey;
256257
commandLine.push_back("-swift-module-file=" + depModuleID.ModuleName + "=" +
257258
path);
259+
// Add the exported macro from interface into current module.
260+
llvm::for_each(
261+
interfaceDepDetails->textualModuleDetails.macroDependencies,
262+
[&](const auto &entry) {
263+
dependencyInfoCopy.addMacroDependency(entry.first(),
264+
entry.second.LibraryPath,
265+
entry.second.ExecutablePath);
266+
});
258267
} break;
259268
case swift::ModuleDependencyKind::SwiftBinary: {
260269
auto binaryDepDetails = depInfo.getAsSwiftBinaryModule();
@@ -319,7 +328,6 @@ static llvm::Error resolveExplicitModuleInputs(
319328
}
320329

321330
// Update the dependency in the cache with the modified command-line.
322-
auto dependencyInfoCopy = resolvingDepInfo;
323331
if (resolvingDepInfo.isSwiftInterfaceModule() ||
324332
resolvingDepInfo.isClangModule()) {
325333
if (service.hasPathMapping())
@@ -345,6 +353,10 @@ static llvm::Error resolveExplicitModuleInputs(
345353
llvm::for_each(
346354
sourceDep->auxiliaryFiles,
347355
[&tracker](const std::string &file) { tracker->trackFile(file); });
356+
llvm::for_each(sourceDep->textualModuleDetails.macroDependencies,
357+
[&tracker](const auto &entry) {
358+
tracker->trackFile(entry.second.LibraryPath);
359+
});
348360
auto root = tracker->createTreeFromDependencies();
349361
if (!root)
350362
return root.takeError();

lib/Sema/TypeCheckMacros.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ static llvm::Expected<CompilerPlugin *>
239239
initializePlugin(ASTContext &ctx, CompilerPlugin *plugin, StringRef libraryPath,
240240
Identifier moduleName) {
241241
// Lock the plugin while initializing.
242-
// Note that'executablePlugn' can be shared between multiple ASTContext.
242+
// Note that 'executablePlugin' can be shared between multiple ASTContext.
243243
plugin->lock();
244244
SWIFT_DEFER { plugin->unlock(); };
245245

test/CAS/macro_plugin_external.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
// RUN: %empty-directory(%t)
77
// RUN: %empty-directory(%t/plugins)
8+
// RUN: split-file %s %t
89
//
910
//== Build the plugin library
1011
// RUN: %host-build-swift \
@@ -15,9 +16,17 @@
1516
// RUN: %S/../Macros/Inputs/syntax_macro_definitions.swift \
1617
// RUN: -g -no-toolchain-stdlib-rpath
1718

19+
/// No macro plugin when macro not used.
1820
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-serialized -module-name MyApp -module-cache-path %t/clang-module-cache -O \
1921
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
20-
// RUN: %s -o %t/deps.json -swift-version 5 -cache-compile-job -cas-path %t/cas -external-plugin-path %t/plugins#%swift-plugin-server
22+
// RUN: %t/nomacro.swift -o %t/deps1.json -swift-version 5 -cache-compile-job -cas-path %t/cas -external-plugin-path %t/plugins#%swift-plugin-server
23+
// RUN: %S/Inputs/SwiftDepsExtractor.py %t/deps1.json MyApp casFSRootID > %t/no_macro_fs.casid
24+
// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/no_macro_fs.casid | %FileCheck %s --check-prefix=NO-MACRO
25+
// NO-MACRO-NOT: MacroDefinition
26+
27+
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-serialized -module-name MyApp -module-cache-path %t/clang-module-cache -O \
28+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
29+
// RUN: %t/macro.swift -o %t/deps.json -swift-version 5 -cache-compile-job -cas-path %t/cas -external-plugin-path %t/plugins#%swift-plugin-server
2130

2231
// RUN: %S/Inputs/SwiftDepsExtractor.py %t/deps.json MyApp casFSRootID > %t/fs.casid
2332
// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/fs.casid | %FileCheck %s --check-prefix=FS
@@ -37,8 +46,12 @@
3746
// RUN: -external-plugin-path %t/plugins/#%swift-plugin-server \
3847
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
3948
// RUN: -module-name MyApp -explicit-swift-module-map-file @%t/map.casid \
40-
// RUN: %s @%t/MyApp.cmd
49+
// RUN: %t/macro.swift @%t/MyApp.cmd
50+
51+
//--- nomacro.swift
52+
func test() {}
4153

54+
//--- macro.swift
4255
@attached(extension, conformances: P, names: named(requirement))
4356
macro DelegatedConformance() = #externalMacro(module: "MacroDefinition", type: "DelegatedConformanceViaExtensionMacro")
4457

0 commit comments

Comments
 (0)