Skip to content

Commit 61c0827

Browse files
committed
[Serialization] Refactor logic deciding transitive module loading logic
Refactor and centralize the logic about how implementation-only and package-only dependencies should be loaded.
1 parent 2bc92a6 commit 61c0827

File tree

5 files changed

+99
-21
lines changed

5 files changed

+99
-21
lines changed

lib/Serialization/ModuleFile.cpp

Lines changed: 23 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;

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: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,3 +1680,46 @@ 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 == getModulePackageName()) {
1716+
return ModuleLoadingBehavior::Required;
1717+
} else {
1718+
return ModuleLoadingBehavior::Ignored;
1719+
}
1720+
}
1721+
1722+
// By default, imports are required dependencies.
1723+
return ModuleLoadingBehavior::Required;
1724+
}
1725+

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: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -940,10 +940,11 @@ void swift::serialization::diagnoseSerializedASTLoadFailure(
940940
std::copy_if(
941941
loadedModuleFile->getDependencies().begin(),
942942
loadedModuleFile->getDependencies().end(), std::back_inserter(missing),
943-
[&duplicates, &Ctx](const ModuleFile::Dependency &dependency) -> bool {
943+
[&duplicates, &loadedModuleFile](
944+
const ModuleFile::Dependency &dependency) -> bool {
944945
if (dependency.isLoaded() || dependency.isHeader() ||
945-
(dependency.isImplementationOnly() &&
946-
Ctx.LangOpts.DebuggerSupport)) {
946+
loadedModuleFile->getTransitiveLoadingBehavior(dependency) !=
947+
ModuleLoadingBehavior::Required) {
947948
return false;
948949
}
949950
return duplicates.insert(dependency.Core.RawPath).second;

0 commit comments

Comments
 (0)