Skip to content

Commit 526ea54

Browse files
committed
[Serialization] Preserve @_implementationOnly through module merging
When we build incrementally, we produce "partial swiftmodules" for each input source file, then merge them together into the final compiled module that, among other things, gets used for debugging. Without this, we'd drop @_implementationOnly imports and any types from the modules that were imported during the module-merging step and then be unable to debug those types
1 parent 0ba6c49 commit 526ea54

File tree

7 files changed

+57
-13
lines changed

7 files changed

+57
-13
lines changed

include/swift/Serialization/ModuleFile.h

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -659,11 +659,26 @@ class ModuleFile
659659
// Out of line to avoid instantiation OnDiskChainedHashTable here.
660660
~ModuleFile();
661661

662-
/// Associates this module file with an AST module.
663-
///
664-
/// Returns any error that occurred during association, including validation
665-
/// that the module file is compatible with the module it's being loaded as.
666-
Status associateWithFileContext(FileUnit *file, SourceLoc diagLoc);
662+
/// Associates this module file with the AST node representing it.
663+
///
664+
/// Checks that the file is compatible with the AST module it's being loaded
665+
/// into, loads any dependencies needed to understand the module, and updates
666+
/// the ASTContext and ClangImporter with search paths and other information
667+
/// from the module.
668+
///
669+
/// \param file The FileUnit that represents this file's place in the AST.
670+
/// \param diagLoc A location used for diagnostics that occur during loading.
671+
/// This does not include diagnostics about \e this file failing to load,
672+
/// but rather other things that might be imported as part of bringing the
673+
/// file into the AST.
674+
/// \param treatAsPartialModule If true, processes implementation-only
675+
/// information instead of assuming the client won't need it and shouldn't
676+
/// see it.
677+
///
678+
/// \returns any error that occurred during association, such as being
679+
/// compiled for a different OS.
680+
Status associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
681+
bool treatAsPartialModule);
667682

668683
/// Checks whether this module can be used.
669684
Status getStatus() const {

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class SerializedModuleLoaderBase : public ModuleLoader {
9494
FileUnit *loadAST(ModuleDecl &M, Optional<SourceLoc> diagLoc,
9595
std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer,
9696
std::unique_ptr<llvm::MemoryBuffer> moduleDocInputBuffer,
97-
bool isFramework = false);
97+
bool isFramework, bool treatAsPartialModule);
9898

9999
/// Check whether the module with a given name can be imported without
100100
/// importing it.

lib/Frontend/Frontend.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,8 @@ bool CompilerInstance::parsePartialModulesAndLibraryFiles(
868868
for (auto &PM : PartialModules) {
869869
assert(PM.ModuleBuffer);
870870
if (!SML->loadAST(*MainModule, SourceLoc(), std::move(PM.ModuleBuffer),
871-
std::move(PM.ModuleDocBuffer)))
871+
std::move(PM.ModuleDocBuffer), /*isFramework*/false,
872+
/*treatAsPartialModule*/true))
872873
hadLoadError = true;
873874
}
874875

lib/Serialization/ModuleFile.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,7 +1460,8 @@ ModuleFile::ModuleFile(
14601460
}
14611461

14621462
Status ModuleFile::associateWithFileContext(FileUnit *file,
1463-
SourceLoc diagLoc) {
1463+
SourceLoc diagLoc,
1464+
bool treatAsPartialModule) {
14641465
PrettyStackTraceModuleFile stackEntry(*this);
14651466

14661467
assert(getStatus() == Status::Valid && "invalid module file");
@@ -1513,7 +1514,7 @@ Status ModuleFile::associateWithFileContext(FileUnit *file,
15131514
continue;
15141515
}
15151516

1516-
if (dependency.isImplementationOnly())
1517+
if (dependency.isImplementationOnly() && !treatAsPartialModule)
15171518
continue;
15181519

15191520
StringRef modulePathStr = dependency.RawPath;

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ FileUnit *SerializedModuleLoaderBase::loadAST(
366366
ModuleDecl &M, Optional<SourceLoc> diagLoc,
367367
std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer,
368368
std::unique_ptr<llvm::MemoryBuffer> moduleDocInputBuffer,
369-
bool isFramework) {
369+
bool isFramework, bool treatAsPartialModule) {
370370
assert(moduleInputBuffer);
371371

372372
StringRef moduleBufferID = moduleInputBuffer->getBufferIdentifier();
@@ -402,7 +402,8 @@ FileUnit *SerializedModuleLoaderBase::loadAST(
402402

403403
auto diagLocOrInvalid = diagLoc.getValueOr(SourceLoc());
404404
loadInfo.status =
405-
loadedModuleFile->associateWithFileContext(fileUnit, diagLocOrInvalid);
405+
loadedModuleFile->associateWithFileContext(fileUnit, diagLocOrInvalid,
406+
treatAsPartialModule);
406407
if (loadInfo.status == serialization::Status::Valid) {
407408
Ctx.bumpGeneration();
408409
LoadedModuleFiles.emplace_back(std::move(loadedModuleFile),
@@ -656,7 +657,8 @@ ModuleDecl *SerializedModuleLoaderBase::loadModule(SourceLoc importLoc,
656657
SWIFT_DEFER { M->setHasResolvedImports(); };
657658

658659
if (!loadAST(*M, moduleID.second, std::move(moduleInputBuffer),
659-
std::move(moduleDocInputBuffer), isFramework)) {
660+
std::move(moduleDocInputBuffer), isFramework,
661+
/*treatAsPartialModule*/false)) {
660662
M->setFailedToLoad();
661663
}
662664

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// This is a very simple test that module merging does not eliminate
2+
// @_implementationOnly imports or declarations referenced from those imports.
3+
// More thorough tests exist in LLDB, which can look into those imports when
4+
// debugging a client of the module with @_implementationOnly imports.
5+
6+
// RUN: %empty-directory(%t)
7+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/def_struct.swift
8+
9+
// RUN: %target-swift-frontend -emit-module -I %t -o %t/main~partial.swiftmodule -module-name main %s
10+
// RUN: llvm-bcanalyzer -dump %t/main~partial.swiftmodule | %FileCheck %s
11+
// RUN: grep -q TwoInts %t/main~partial.swiftmodule
12+
13+
// RUN: %target-swift-frontend -merge-modules -emit-module -I %t -o %t/main.swiftmodule %t/main~partial.swiftmodule
14+
// RUN: llvm-bcanalyzer -dump %t/main.swiftmodule | %FileCheck %s
15+
// RUN: grep -q TwoInts %t/main.swiftmodule
16+
17+
@_implementationOnly import def_struct
18+
19+
struct Container {
20+
var wrapped: TwoInts
21+
}
22+
23+
// CHECK: <IMPORTED_MODULE abbrevid={{[0-9]+}} op0=2 op1=0{{.*}}/> blob data = 'def_struct'

tools/SourceKit/lib/SwiftLang/SwiftIndexing.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ static void indexModule(llvm::MemoryBuffer *Input,
190190
// documentation file.
191191
// FIXME: refactor the frontend to provide an easy way to figure out the
192192
// correct filename here.
193-
auto FUnit = Loader->loadAST(*Mod, None, std::move(Buf), nullptr);
193+
auto FUnit = Loader->loadAST(*Mod, None, std::move(Buf), nullptr,
194+
/*isFramework*/false,
195+
/*treatAsPartialModule*/false);
194196

195197
// FIXME: Not knowing what went wrong is pretty bad. loadModule() should be
196198
// more modular, rather than emitting diagnostics itself.

0 commit comments

Comments
 (0)