Skip to content

Commit e010d7d

Browse files
authored
Merge pull request #64487 from xymus/transitive-loading-refactor
[Serialization] Refactor how we decide to load transitive dependencies
2 parents c9beaf0 + 8aadcf4 commit e010d7d

File tree

5 files changed

+117
-29
lines changed

5 files changed

+117
-29
lines changed

lib/Serialization/ModuleFile.cpp

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -194,23 +194,11 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
194194
continue;
195195
}
196196

197-
// If this module file is being installed into the main module, it's treated
198-
// as a partial module.
199-
auto isPartialModule = M->isMainModule();
200-
201-
if (dependency.isImplementationOnly() &&
202-
!(isPartialModule || ctx.LangOpts.DebuggerSupport)) {
203-
// When building normally (and not merging partial modules), we don't
204-
// want to bring in the implementation-only module, because that might
205-
// change the set of visible declarations. However, when debugging we
206-
// want to allow getting at the internals of this module when possible,
207-
// and so we'll try to reference the implementation-only module if it's
208-
// available.
209-
continue;
210-
}
197+
ModuleLoadingBehavior transitiveBehavior =
198+
getTransitiveLoadingBehavior(dependency);
211199

212-
if (dependency.isPackageOnly() &&
213-
ctx.LangOpts.PackageName != this->getModulePackageName())
200+
// Skip this dependency?
201+
if (transitiveBehavior == ModuleLoadingBehavior::Ignored)
214202
continue;
215203

216204
ImportPath::Builder builder(ctx, dependency.Core.RawPath,
@@ -230,11 +218,13 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
230218
modulePath.front().Item == file->getParentModule()->getName()) {
231219
return error(Status::MissingUnderlyingModule);
232220
}
233-
234221
// Otherwise, continue trying to load dependencies, so that we can list
235222
// everything that's missing.
236-
if (!(dependency.isImplementationOnly() && ctx.LangOpts.DebuggerSupport))
223+
224+
// Report a missing dependency only when really needed.
225+
if (transitiveBehavior == ModuleLoadingBehavior::Required)
237226
missingDependency = true;
227+
238228
continue;
239229
}
240230

@@ -269,6 +259,21 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
269259
return status;
270260
}
271261

262+
ModuleLoadingBehavior
263+
ModuleFile::getTransitiveLoadingBehavior(const Dependency &dependency) const {
264+
ASTContext &ctx = getContext();
265+
ModuleDecl *mod = FileContext->getParentModule();
266+
267+
// If this module file is being installed into the main module, it's treated
268+
// as a partial module.
269+
auto isPartialModule = mod->isMainModule();
270+
271+
return Core->getTransitiveLoadingBehavior(dependency.Core,
272+
ctx.LangOpts.DebuggerSupport,
273+
isPartialModule,
274+
ctx.LangOpts.PackageName);
275+
}
276+
272277
bool ModuleFile::mayHaveDiagnosticsPointingAtBuffer() const {
273278
if (!hasError())
274279
return false;
@@ -463,6 +468,10 @@ void ModuleFile::getImportedModules(SmallVectorImpl<ImportedModule> &results,
463468
continue;
464469
}
465470

471+
} else if (dep.isPackageOnly()) {
472+
if (!filter.contains(ModuleDecl::ImportFilterKind::PackageOnly))
473+
continue;
474+
466475
} else {
467476
if (!filter.contains(ModuleDecl::ImportFilterKind::Default))
468477
continue;

lib/Serialization/ModuleFile.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,10 @@ class ModuleFile
646646
Status associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
647647
bool recoverFromIncompatibility);
648648

649+
/// How should \p dependency be loaded for a transitive import via \c this?
650+
ModuleLoadingBehavior
651+
getTransitiveLoadingBehavior(const Dependency &dependency) const;
652+
649653
/// Returns `true` if there is a buffer that might contain source code where
650654
/// other parts of the compiler could have emitted diagnostics, to indicate
651655
/// that the object must be kept alive as long as the diagnostics exist.

lib/Serialization/ModuleFileSharedCore.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,3 +1680,48 @@ ModuleFileSharedCore::ModuleFileSharedCore(
16801680
bool ModuleFileSharedCore::hasSourceInfo() const {
16811681
return !!DeclUSRsTable;
16821682
}
1683+
1684+
ModuleLoadingBehavior
1685+
ModuleFileSharedCore::getTransitiveLoadingBehavior(
1686+
const Dependency &dependency,
1687+
bool debuggerMode,
1688+
bool isPartialModule,
1689+
StringRef packageName) const {
1690+
if (isPartialModule) {
1691+
// Keep the merge-module behavior for legacy support. In that case
1692+
// we load all transitive dependencies from partial modules and
1693+
// error if it fails.
1694+
return ModuleLoadingBehavior::Required;
1695+
}
1696+
1697+
if (dependency.isImplementationOnly()) {
1698+
// Implementation-only dependencies are not usually loaded from
1699+
// transitive imports.
1700+
if (debuggerMode) {
1701+
// In the debugger, try to load the module if possible.
1702+
// Same in the case of a testable import, try to load the dependency
1703+
// but don't fail if it's missing as this could be source breaking.
1704+
return ModuleLoadingBehavior::Optional;
1705+
} else {
1706+
// When building normally, ignore transitive implementation-only
1707+
// imports.
1708+
return ModuleLoadingBehavior::Ignored;
1709+
}
1710+
}
1711+
1712+
if (dependency.isPackageOnly()) {
1713+
// Package dependencies are usually loaded only for import from the same
1714+
// package.
1715+
if (!packageName.empty() && packageName == getModulePackageName()) {
1716+
return ModuleLoadingBehavior::Required;
1717+
} else if (debuggerMode) {
1718+
return ModuleLoadingBehavior::Optional;
1719+
} else {
1720+
return ModuleLoadingBehavior::Ignored;
1721+
}
1722+
}
1723+
1724+
// By default, imports are required dependencies.
1725+
return ModuleLoadingBehavior::Required;
1726+
}
1727+

lib/Serialization/ModuleFileSharedCore.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ namespace llvm {
2525

2626
namespace swift {
2727

28+
/// How a dependency should be loaded.
29+
///
30+
/// \sa getTransitiveLoadingBehavior
31+
enum class ModuleLoadingBehavior {
32+
Required,
33+
Optional,
34+
Ignored
35+
};
36+
2837
/// Serialized core data of a module. The difference with `ModuleFile` is that
2938
/// `ModuleFileSharedCore` provides immutable data and is independent of a
3039
/// particular ASTContext. It is designed to be able to be shared across
@@ -581,6 +590,22 @@ class ModuleFileSharedCore {
581590
bool hasSourceInfo() const;
582591

583592
bool isConcurrencyChecked() const { return Bits.IsConcurrencyChecked; }
593+
594+
/// How should \p dependency be loaded for a transitive import via \c this?
595+
///
596+
/// If \p debuggerMode, more transitive dependencies should try to be loaded
597+
/// as they can be useful in debugging.
598+
///
599+
/// If \p isPartialModule, transitive dependencies should be loaded as we're
600+
/// in merge-module mode.
601+
///
602+
/// If \p packageName is set, transitive package dependencies are loaded if
603+
/// loaded from the same package.
604+
ModuleLoadingBehavior
605+
getTransitiveLoadingBehavior(const Dependency &dependency,
606+
bool debuggerMode,
607+
bool isPartialModule,
608+
StringRef packageName) const;
584609
};
585610

586611
template <typename T, typename RawData>

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -420,14 +420,18 @@ llvm::ErrorOr<ModuleDependencyInfo> SerializedModuleLoaderBase::scanModuleFile(
420420
if (dependency.isHeader())
421421
continue;
422422

423-
// Transitive @_implementationOnly dependencies of
424-
// binary modules are not required to be imported during normal builds
425-
// TODO: This is worth revisiting for debugger purposes
426-
if (dependency.isImplementationOnly())
427-
continue;
428-
429-
if (dependency.isPackageOnly() &&
430-
Ctx.LangOpts.PackageName != loadedModuleFile->getModulePackageName())
423+
// Some transitive dependencies of binary modules are not required to be
424+
// imported during normal builds.
425+
// TODO: This is worth revisiting for debugger purposes where
426+
// loading the module is optional, and implementation-only imports
427+
// from modules with testing enabled where the dependency is
428+
// optional.
429+
ModuleLoadingBehavior transitiveBehavior =
430+
loadedModuleFile->getTransitiveLoadingBehavior(dependency,
431+
/*debuggerMode*/false,
432+
/*isPartialModule*/false,
433+
/*package*/Ctx.LangOpts.PackageName);
434+
if (transitiveBehavior != ModuleLoadingBehavior::Required)
431435
continue;
432436

433437
// Find the top-level module name.
@@ -940,10 +944,11 @@ void swift::serialization::diagnoseSerializedASTLoadFailure(
940944
std::copy_if(
941945
loadedModuleFile->getDependencies().begin(),
942946
loadedModuleFile->getDependencies().end(), std::back_inserter(missing),
943-
[&duplicates, &Ctx](const ModuleFile::Dependency &dependency) -> bool {
947+
[&duplicates, &loadedModuleFile](
948+
const ModuleFile::Dependency &dependency) -> bool {
944949
if (dependency.isLoaded() || dependency.isHeader() ||
945-
(dependency.isImplementationOnly() &&
946-
Ctx.LangOpts.DebuggerSupport)) {
950+
loadedModuleFile->getTransitiveLoadingBehavior(dependency) !=
951+
ModuleLoadingBehavior::Required) {
947952
return false;
948953
}
949954
return duplicates.insert(dependency.Core.RawPath).second;

0 commit comments

Comments
 (0)