Skip to content

Commit 3425206

Browse files
authored
Merge pull request #64794 from xymus/cherry-pick-internal-import
[5.9][Serialization] Load non-public transitive dependencies on @testable imports
2 parents 93e77f3 + 171376a commit 3425206

14 files changed

+308
-116
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,11 @@ REMARK(module_loaded,none,
10411041
"%select{| (overlay for a clang dependency)}1"
10421042
"; source: '%2', loaded: '%3'",
10431043
(Identifier, unsigned, StringRef, StringRef))
1044+
1045+
REMARK(transitive_dependency_behavior,none,
1046+
"%1 has %select{a required|an optional|an ignored}2 "
1047+
"transitive dependency on '%0'",
1048+
(StringRef, Identifier, unsigned))
10441049

10451050
REMARK(explicit_interface_build_skipped,none,
10461051
"Skipped rebuilding module at %0 - up-to-date",

include/swift/AST/FileUnit.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,9 @@ class FileUnit : public DeclContext, public ASTAllocated<FileUnit> {
294294
virtual void
295295
collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const {}
296296

297+
/// Load extra dependencies of this module to satisfy a testable import.
298+
virtual void loadDependenciesForTestable(SourceLoc diagLoc) const {}
299+
297300
/// Returns the path of the file or directory that defines the module
298301
/// represented by this \c FileUnit, or empty string if there is none.
299302
/// Cross-import overlay specifiers are found relative to this path.

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,8 @@ class SerializedASTFile final : public LoadedFile {
454454
virtual void
455455
collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const override;
456456

457+
virtual void loadDependenciesForTestable(SourceLoc diagLoc) const override;
458+
457459
Identifier getDiscriminatorForPrivateValue(const ValueDecl *D) const override;
458460

459461
virtual StringRef getFilename() const override;

include/swift/Serialization/Validation.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,23 @@ void diagnoseSerializedASTLoadFailure(
251251
StringRef moduleBufferID, StringRef moduleDocBufferID,
252252
ModuleFile *loadedModuleFile, Identifier ModuleName);
253253

254+
/// Emit diagnostics explaining a failure to load a serialized AST,
255+
/// this version only supports diagnostics relevant to transitive dependencies:
256+
/// missing dependency, missing underlying module, or circular dependency.
257+
///
258+
/// \see diagnoseSerializedASTLoadFailure that supports all diagnostics.
259+
///
260+
/// - \p Ctx is an AST context through which any diagnostics are surfaced.
261+
/// - \p diagLoc is the (possibly invalid) location used in the diagnostics.
262+
/// - \p status describes the issue with loading the AST. It must not be
263+
/// Status::Valid.
264+
/// - \p loadedModuleFile is an invalid loaded module.
265+
/// - \p ModuleName is the name used to refer to the module in diagnostics.
266+
/// - \p forTestable indicates if we loaded the AST for a @testable import.
267+
void diagnoseSerializedASTLoadFailureTransitive(
268+
ASTContext &Ctx, SourceLoc diagLoc, const serialization::Status status,
269+
ModuleFile *loadedModuleFile, Identifier ModuleName, bool forTestable);
270+
254271
} // end namespace serialization
255272
} // end namespace swift
256273

lib/Sema/ImportResolution.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,15 @@ void ImportResolver::bindImport(UnboundImport &&I) {
324324
return;
325325
}
326326

327+
// Load more dependencies for testable imports.
328+
if (I.import.options.contains(ImportFlags::Testable)) {
329+
SourceLoc diagLoc;
330+
if (ID) diagLoc = ID.get()->getStartLoc();
331+
332+
for (auto file: M->getFiles())
333+
file->loadDependenciesForTestable(diagLoc);
334+
}
335+
327336
auto topLevelModule = I.getTopLevelModule(M, SF);
328337

329338
I.validateOptions(topLevelModule, SF);

lib/Serialization/ModuleFile.cpp

Lines changed: 75 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "ModuleFormat.h"
1818
#include "swift/Serialization/SerializationOptions.h"
1919
#include "swift/Subsystems.h"
20+
#include "swift/AST/DiagnosticsSema.h"
2021
#include "swift/AST/ASTContext.h"
2122
#include "swift/AST/ASTMangler.h"
2223
#include "swift/AST/GenericSignature.h"
@@ -122,56 +123,19 @@ bool ModuleFile::allowCompilerErrors() const {
122123
return getContext().LangOpts.AllowModuleWithCompilerErrors;
123124
}
124125

125-
Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
126-
bool recoverFromIncompatibility) {
127-
PrettyStackTraceModuleFile stackEntry(*this);
128-
129-
assert(!hasError() && "error already detected; should not call this");
130-
assert(!FileContext && "already associated with an AST module");
131-
FileContext = file;
132-
Status status = Status::Valid;
133-
134-
ModuleDecl *M = file->getParentModule();
135-
// The real (on-disk) name of the module should be checked here as that's the
136-
// actually loaded module. In case module aliasing is used when building the main
137-
// module, e.g. -module-name MyModule -module-alias Foo=Bar, the loaded module
138-
// that maps to 'Foo' is actually Bar.swiftmodule|.swiftinterface (applies to swift
139-
// modules only), which is retrieved via M->getRealName(). If no module aliasing is
140-
// used, M->getRealName() will return the same value as M->getName(), which is 'Foo'.
141-
if (M->getRealName().str() != Core->Name) {
142-
return error(Status::NameMismatch);
143-
}
144-
126+
Status
127+
ModuleFile::loadDependenciesForFileContext(const FileUnit *file,
128+
SourceLoc diagLoc,
129+
bool forTestable) {
145130
ASTContext &ctx = getContext();
146-
147-
llvm::Triple moduleTarget(llvm::Triple::normalize(Core->TargetTriple));
148-
if (!areCompatibleArchitectures(moduleTarget, ctx.LangOpts.Target) ||
149-
!areCompatibleOSs(moduleTarget, ctx.LangOpts.Target)) {
150-
status = Status::TargetIncompatible;
151-
if (!recoverFromIncompatibility)
152-
return error(status);
153-
} else if (ctx.LangOpts.EnableTargetOSChecking && !M->isResilient() &&
154-
isTargetTooNew(moduleTarget, ctx.LangOpts.Target)) {
155-
status = Status::TargetTooNew;
156-
if (!recoverFromIncompatibility)
157-
return error(status);
158-
}
159-
160-
StringRef SDKPath = ctx.SearchPathOpts.getSDKPath();
161-
if (SDKPath.empty() ||
162-
!Core->ModuleInputBuffer->getBufferIdentifier().startswith(SDKPath)) {
163-
for (const auto &searchPath : Core->SearchPaths) {
164-
ctx.addSearchPath(
165-
ctx.SearchPathOpts.SearchPathRemapper.remapPath(searchPath.Path),
166-
searchPath.IsFramework,
167-
searchPath.IsSystem);
168-
}
169-
}
170-
171131
auto clangImporter = static_cast<ClangImporter *>(ctx.getClangModuleLoader());
132+
ModuleDecl *M = file->getParentModule();
172133

173134
bool missingDependency = false;
174135
for (auto &dependency : Dependencies) {
136+
if (forTestable && dependency.isLoaded())
137+
continue;
138+
175139
assert(!dependency.isLoaded() && "already loaded?");
176140

177141
if (dependency.isHeader()) {
@@ -195,7 +159,15 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
195159
}
196160

197161
ModuleLoadingBehavior transitiveBehavior =
198-
getTransitiveLoadingBehavior(dependency);
162+
getTransitiveLoadingBehavior(dependency, forTestable);
163+
164+
if (ctx.LangOpts.EnableModuleLoadingRemarks) {
165+
ctx.Diags.diagnose(diagLoc,
166+
diag::transitive_dependency_behavior,
167+
dependency.Core.getPrettyPrintedPath(),
168+
M->getName(),
169+
unsigned(transitiveBehavior));
170+
}
199171

200172
// Skip this dependency?
201173
if (transitiveBehavior == ModuleLoadingBehavior::Ignored)
@@ -250,6 +222,59 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
250222
return error(Status::MissingDependency);
251223
}
252224

225+
return Status::Valid;
226+
}
227+
228+
Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
229+
bool recoverFromIncompatibility) {
230+
PrettyStackTraceModuleFile stackEntry(*this);
231+
232+
assert(!hasError() && "error already detected; should not call this");
233+
assert(!FileContext && "already associated with an AST module");
234+
FileContext = file;
235+
Status status = Status::Valid;
236+
237+
ModuleDecl *M = file->getParentModule();
238+
// The real (on-disk) name of the module should be checked here as that's the
239+
// actually loaded module. In case module aliasing is used when building the main
240+
// module, e.g. -module-name MyModule -module-alias Foo=Bar, the loaded module
241+
// that maps to 'Foo' is actually Bar.swiftmodule|.swiftinterface (applies to swift
242+
// modules only), which is retrieved via M->getRealName(). If no module aliasing is
243+
// used, M->getRealName() will return the same value as M->getName(), which is 'Foo'.
244+
if (M->getRealName().str() != Core->Name) {
245+
return error(Status::NameMismatch);
246+
}
247+
248+
ASTContext &ctx = getContext();
249+
250+
llvm::Triple moduleTarget(llvm::Triple::normalize(Core->TargetTriple));
251+
if (!areCompatibleArchitectures(moduleTarget, ctx.LangOpts.Target) ||
252+
!areCompatibleOSs(moduleTarget, ctx.LangOpts.Target)) {
253+
status = Status::TargetIncompatible;
254+
if (!recoverFromIncompatibility)
255+
return error(status);
256+
} else if (ctx.LangOpts.EnableTargetOSChecking && !M->isResilient() &&
257+
isTargetTooNew(moduleTarget, ctx.LangOpts.Target)) {
258+
status = Status::TargetTooNew;
259+
if (!recoverFromIncompatibility)
260+
return error(status);
261+
}
262+
263+
StringRef SDKPath = ctx.SearchPathOpts.getSDKPath();
264+
if (SDKPath.empty() ||
265+
!Core->ModuleInputBuffer->getBufferIdentifier().startswith(SDKPath)) {
266+
for (const auto &searchPath : Core->SearchPaths) {
267+
ctx.addSearchPath(
268+
ctx.SearchPathOpts.SearchPathRemapper.remapPath(searchPath.Path),
269+
searchPath.IsFramework,
270+
searchPath.IsSystem);
271+
}
272+
}
273+
274+
Status res = loadDependenciesForFileContext(file, diagLoc,
275+
/*forTestable=*/false);
276+
if (res != Status::Valid) return res;
277+
253278
if (Core->Bits.HasEntryPoint) {
254279
FileContext->getParentModule()->registerEntryPointFile(FileContext,
255280
SourceLoc(),
@@ -260,7 +285,8 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
260285
}
261286

262287
ModuleLoadingBehavior
263-
ModuleFile::getTransitiveLoadingBehavior(const Dependency &dependency) const {
288+
ModuleFile::getTransitiveLoadingBehavior(const Dependency &dependency,
289+
bool forTestable) const {
264290
ASTContext &ctx = getContext();
265291
ModuleDecl *mod = FileContext->getParentModule();
266292

@@ -271,7 +297,8 @@ ModuleFile::getTransitiveLoadingBehavior(const Dependency &dependency) const {
271297
return Core->getTransitiveLoadingBehavior(dependency.Core,
272298
ctx.LangOpts.DebuggerSupport,
273299
isPartialModule,
274-
ctx.LangOpts.PackageName);
300+
ctx.LangOpts.PackageName,
301+
forTestable);
275302
}
276303

277304
bool ModuleFile::mayHaveDiagnosticsPointingAtBuffer() const {

lib/Serialization/ModuleFile.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,9 +649,23 @@ class ModuleFile
649649
Status associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
650650
bool recoverFromIncompatibility);
651651

652+
/// Load dependencies of this module.
653+
///
654+
/// \param file The FileUnit that represents this file's place in the AST.
655+
/// \param diagLoc A location used for diagnostics that occur during loading.
656+
/// This does not include diagnostics about \e this file failing to load,
657+
/// but rather other things that might be imported as part of bringing the
658+
/// file into the AST.
659+
///
660+
/// \returns any error that occurred during loading dependencies.
661+
Status
662+
loadDependenciesForFileContext(const FileUnit *file, SourceLoc diagLoc,
663+
bool forTestable);
664+
652665
/// How should \p dependency be loaded for a transitive import via \c this?
653666
ModuleLoadingBehavior
654-
getTransitiveLoadingBehavior(const Dependency &dependency) const;
667+
getTransitiveLoadingBehavior(const Dependency &dependency,
668+
bool forTestable) const;
655669

656670
/// Returns `true` if there is a buffer that might contain source code where
657671
/// other parts of the compiler could have emitted diagnostics, to indicate

lib/Serialization/ModuleFileSharedCore.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,7 +1688,8 @@ ModuleFileSharedCore::getTransitiveLoadingBehavior(
16881688
const Dependency &dependency,
16891689
bool debuggerMode,
16901690
bool isPartialModule,
1691-
StringRef packageName) const {
1691+
StringRef packageName,
1692+
bool forTestable) const {
16921693
if (isPartialModule) {
16931694
// Keep the merge-module behavior for legacy support. In that case
16941695
// we load all transitive dependencies from partial modules and
@@ -1717,7 +1718,7 @@ ModuleFileSharedCore::getTransitiveLoadingBehavior(
17171718
// Non-public imports are similar to implementation-only, the module
17181719
// loading behavior differs on loading those dependencies
17191720
// on testable imports.
1720-
if (isTestable() || !moduleIsResilient) {
1721+
if (forTestable || !moduleIsResilient) {
17211722
return ModuleLoadingBehavior::Required;
17221723
} else if (debuggerMode) {
17231724
return ModuleLoadingBehavior::Optional;
@@ -1730,6 +1731,7 @@ ModuleFileSharedCore::getTransitiveLoadingBehavior(
17301731
// Package dependencies are usually loaded only for import from the same
17311732
// package.
17321733
if ((!packageName.empty() && packageName == getModulePackageName()) ||
1734+
forTestable ||
17331735
!moduleIsResilient) {
17341736
return ModuleLoadingBehavior::Required;
17351737
} else if (debuggerMode) {

lib/Serialization/ModuleFileSharedCore.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,11 +614,17 @@ class ModuleFileSharedCore {
614614
///
615615
/// If \p packageName is set, transitive package dependencies are loaded if
616616
/// loaded from the same package.
617+
///
618+
/// If \p forTestable, get the desired loading behavior for a @testable
619+
/// import. Reports non-public dependencies as required for a testable
620+
/// client so it can access internal details, which in turn can reference
621+
/// those non-public dependencies.
617622
ModuleLoadingBehavior
618623
getTransitiveLoadingBehavior(const Dependency &dependency,
619624
bool debuggerMode,
620625
bool isPartialModule,
621-
StringRef packageName) const;
626+
StringRef packageName,
627+
bool forTestable) const;
622628
};
623629

624630
template <typename T, typename RawData>

0 commit comments

Comments
 (0)