Skip to content

Commit 6eed1f9

Browse files
authored
Merge pull request #23248 from dexonsmith/PR-48740787.master
[master] Fix filesystem race in bridging PCH emission/loading
2 parents c49fca5 + 2134b17 commit 6eed1f9

File tree

4 files changed

+106
-4
lines changed

4 files changed

+106
-4
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,6 @@ bool ClangImporter::canReadPCH(StringRef PCHFilename) {
770770
*clangDiags,
771771
CI.getLangOpts(),
772772
clangSrcMgr,
773-
CI.getPCMCache(),
774773
headerSearchInfo,
775774
(clang::ModuleLoader &)CI,
776775
/*IILookup=*/nullptr,
@@ -783,7 +782,7 @@ bool ClangImporter::canReadPCH(StringRef PCHFilename) {
783782
// Note: Reusing the PCHContainerReader or ModuleFileExtensions could be
784783
// dangerous.
785784
std::unique_ptr<clang::ASTReader> Reader(new clang::ASTReader(
786-
PP, &ctx, CI.getPCHContainerReader(),
785+
PP, CI.getModuleCache(), &ctx, CI.getPCHContainerReader(),
787786
CI.getFrontendOpts().ModuleFileExtensions,
788787
CI.getHeaderSearchOpts().Sysroot,
789788
/*DisableValidation*/ false,
@@ -1395,7 +1394,8 @@ std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath,
13951394
invocation->getPreprocessorOpts().resetNonModularOptions();
13961395

13971396
clang::CompilerInstance rewriteInstance(
1398-
Impl.Instance->getPCHContainerOperations());
1397+
Impl.Instance->getPCHContainerOperations(),
1398+
&Impl.Instance->getModuleCache());
13991399
rewriteInstance.setInvocation(invocation);
14001400
rewriteInstance.createDiagnostics(new clang::IgnoringDiagConsumer);
14011401

@@ -1451,9 +1451,11 @@ ClangImporter::emitBridgingPCH(StringRef headerPath,
14511451
invocation->getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH;
14521452
invocation->getPreprocessorOpts().resetNonModularOptions();
14531453
invocation->getLangOpts()->NeededByPCHOrCompilationUsesPCH = true;
1454+
invocation->getLangOpts()->CacheGeneratedPCH = true;
14541455

14551456
clang::CompilerInstance emitInstance(
1456-
Impl.Instance->getPCHContainerOperations());
1457+
Impl.Instance->getPCHContainerOperations(),
1458+
&Impl.Instance->getModuleCache());
14571459
emitInstance.setInvocation(std::move(invocation));
14581460
emitInstance.createDiagnostics(&Impl.Instance->getDiagnosticClient(),
14591461
/*ShouldOwnClient=*/false);

unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ if(SWIFT_INCLUDE_TOOLS)
66

77
add_subdirectory(AST)
88
add_subdirectory(Basic)
9+
add_subdirectory(ClangImporter)
910
add_subdirectory(Driver)
1011
add_subdirectory(FrontendTool)
1112
add_subdirectory(IDE)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
add_swift_unittest(SwiftClangImporterTests
2+
ClangImporterTests.cpp
3+
)
4+
5+
target_link_libraries(SwiftClangImporterTests
6+
PRIVATE
7+
swiftClangImporter
8+
swiftParse
9+
swiftAST
10+
)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include "swift/AST/ASTContext.h"
2+
#include "swift/AST/DiagnosticEngine.h"
3+
#include "swift/AST/SearchPathOptions.h"
4+
#include "swift/Basic/LLVMInitialize.h"
5+
#include "swift/Basic/LangOptions.h"
6+
#include "swift/Basic/SourceManager.h"
7+
#include "swift/ClangImporter/ClangImporter.h"
8+
#include "swift/ClangImporter/ClangImporterOptions.h"
9+
#include "llvm/Support/FileSystem.h"
10+
#include "llvm/Support/Path.h"
11+
#include "llvm/Support/raw_ostream.h"
12+
#include "gtest/gtest.h"
13+
14+
using namespace swift;
15+
16+
static std::string createFilename(StringRef base, StringRef name) {
17+
SmallString<256> path = base;
18+
llvm::sys::path::append(path, name);
19+
return llvm::Twine(path).str();
20+
}
21+
22+
static bool emitFileWithContents(StringRef path, StringRef contents,
23+
std::string *pathOut = nullptr) {
24+
int FD;
25+
if (llvm::sys::fs::openFileForWrite(path, FD))
26+
return true;
27+
if (pathOut)
28+
*pathOut = path;
29+
llvm::raw_fd_ostream file(FD, /*shouldClose=*/true);
30+
file << contents;
31+
return false;
32+
}
33+
34+
static bool emitFileWithContents(StringRef base, StringRef name,
35+
StringRef contents,
36+
std::string *pathOut = nullptr) {
37+
return emitFileWithContents(createFilename(base, name), contents, pathOut);
38+
}
39+
40+
TEST(ClangImporterTest, emitPCHInMemory) {
41+
// Create a temporary cache on disk and clean it up at the end.
42+
ClangImporterOptions options;
43+
SmallString<256> temp;
44+
ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory(
45+
"ClangImporterTest.emitPCHInMemory", temp));
46+
SWIFT_DEFER { llvm::sys::fs::remove_directories(temp); };
47+
48+
// Create a cache subdirectory for the modules and PCH.
49+
std::string cache = createFilename(temp, "cache");
50+
ASSERT_FALSE(llvm::sys::fs::create_directory(cache));
51+
options.ModuleCachePath = cache;
52+
options.PrecompiledHeaderOutputDir = cache;
53+
54+
// Create the includes.
55+
std::string include = createFilename(temp, "include");
56+
ASSERT_FALSE(llvm::sys::fs::create_directory(include));
57+
options.ExtraArgs.emplace_back("-nosysteminc");
58+
options.ExtraArgs.emplace_back((llvm::Twine("-I") + include).str());
59+
ASSERT_FALSE(emitFileWithContents(include, "module.modulemap",
60+
"module A {\n"
61+
" header \"A.h\"\n"
62+
"}\n"));
63+
ASSERT_FALSE(emitFileWithContents(include, "A.h", "int foo(void);\n"));
64+
65+
// Create a bridging header.
66+
ASSERT_FALSE(emitFileWithContents(temp, "bridging.h", "#import <A.h>\n",
67+
&options.BridgingHeader));
68+
69+
// Set up the importer and emit a bridging PCH.
70+
swift::LangOptions langOpts;
71+
langOpts.Target = llvm::Triple("x86_64", "apple", "darwin");
72+
INITIALIZE_LLVM();
73+
swift::SearchPathOptions searchPathOpts;
74+
swift::SourceManager sourceMgr;
75+
swift::DiagnosticEngine diags(sourceMgr);
76+
std::unique_ptr<ASTContext> context(
77+
ASTContext::get(langOpts, searchPathOpts, sourceMgr, diags));
78+
auto importer = ClangImporter::create(*context, options);
79+
80+
std::string PCH = createFilename(cache, "bridging.h.pch");
81+
ASSERT_FALSE(importer->canReadPCH(PCH));
82+
ASSERT_FALSE(importer->emitBridgingPCH(options.BridgingHeader, PCH));
83+
ASSERT_TRUE(importer->canReadPCH(PCH));
84+
85+
// Overwrite the PCH with garbage. We should still be able to read it from
86+
// the in-memory cache.
87+
ASSERT_FALSE(emitFileWithContents(PCH, "garbage"));
88+
ASSERT_TRUE(importer->canReadPCH(PCH));
89+
}

0 commit comments

Comments
 (0)