Skip to content

Commit 0384e36

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 823e809 commit 0384e36

File tree

6 files changed

+122
-85
lines changed

6 files changed

+122
-85
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: 67 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +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"
24+
#include "swift/AST/TypeCheckRequests.h"
2125
#include "swift/Frontend/Frontend.h"
2226
#include "llvm/CAS/CASProvidingFileSystem.h"
2327
#include "llvm/CAS/CachingOnDiskFileSystem.h"
@@ -103,6 +107,20 @@ void ModuleDependencyInfo::addTestableImport(ImportPath::Module module) {
103107
dyn_cast<SwiftSourceModuleDependenciesStorage>(storage.get())->addTestableImport(module);
104108
}
105109

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+
106124
bool ModuleDependencyInfo::isTestableImport(StringRef moduleName) const {
107125
if (auto swiftSourceDepStorage = getAsSwiftSourceModule())
108126
return swiftSourceDepStorage->testableImports.contains(moduleName);
@@ -183,35 +201,55 @@ void ModuleDependencyInfo::addModuleImports(
183201
SmallVector<Decl *, 32> decls;
184202
sourceFile.getTopLevelDecls(decls);
185203
for (auto decl : decls) {
186-
auto importDecl = dyn_cast<ImportDecl>(decl);
187-
if (!importDecl)
188-
continue;
189-
190-
ImportPath::Builder scratch;
191-
auto realPath = importDecl->getRealModulePath(scratch);
192-
193-
// Explicit 'Builtin' import is not a part of the module's
194-
// dependency set, does not exist on the filesystem,
195-
// and is resolved within the compiler during compilation.
196-
SmallString<64> importedModuleName;
197-
realPath.getString(importedModuleName);
198-
if (importedModuleName == BUILTIN_NAME)
199-
continue;
200-
201-
// Ignore/diagnose tautological imports akin to import resolution
202-
if (!swift::dependencies::checkImportNotTautological(
203-
realPath, importDecl->getLoc(), sourceFile,
204-
importDecl->isExported()))
205-
continue;
206-
207-
addModuleImport(realPath, &alreadyAddedModules,
208-
sourceManager, importDecl->getLoc());
209-
210-
// Additionally, keep track of which dependencies of a Source
211-
// module are `@Testable`.
212-
if (getKind() == swift::ModuleDependencyKind::SwiftSource &&
213-
importDecl->isTestable())
214-
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+
CompilerPluginLoadRequest loadRequest{&macroDecl->getASTContext(),
237+
external.moduleName};
238+
CompilerPluginLoadResult loaded =
239+
evaluateOrDefault(macroDecl->getASTContext().evaluator, loadRequest,
240+
CompilerPluginLoadResult::error("request error"));
241+
PluginLoader &loader = ctx.getPluginLoader();
242+
if (auto exe = loaded.getAsExecutablePlugin()) {
243+
// Lookup again to find the library path.
244+
const auto &entry =
245+
loader.lookupPluginByModuleName(external.moduleName);
246+
addMacroDependency(external.moduleName.str(), entry.libraryPath,
247+
entry.executablePath);
248+
} else if (auto plugin = loaded.getAsLibraryPlugin()) {
249+
addMacroDependency(external.moduleName.str(), plugin->getLibraryPath(),
250+
/*executablePath=*/"");
251+
}
252+
}
215253
}
216254

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

670656
void SwiftDependencyTracker::startTracking() {

lib/AST/PluginLoader.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "swift/AST/ASTContext.h"
1515
#include "swift/AST/DiagnosticEngine.h"
1616
#include "swift/AST/DiagnosticsFrontend.h"
17+
#include "swift/Basic/LLVM.h"
1718
#include "swift/Basic/SourceManager.h"
1819
#include "swift/Parse/Lexer.h"
1920
#include "llvm/Config/config.h"

lib/DependencyScan/ScanDependencies.cpp

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

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

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

lib/Sema/TypeCheckMacros.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ initializeExecutablePlugin(ASTContext &ctx,
271271
LoadedExecutablePlugin *executablePlugin,
272272
StringRef libraryPath, Identifier moduleName) {
273273
// Lock the plugin while initializing.
274-
// Note that'executablePlugn' can be shared between multiple ASTContext.
274+
// Note that 'executablePlugin' can be shared between multiple ASTContext.
275275
executablePlugin->lock();
276276
SWIFT_DEFER { executablePlugin->unlock(); };
277277

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)