Skip to content

Commit 7ca683c

Browse files
authored
Merge pull request #8905 from ian-twilightcoder/header-file-info-reuse
[cherry-pick swift/release/6.0][clang][modules] HeaderSearch::MarkFileModuleHeader sets textual headers' HeaderFileInfo non-external when it shouldn't
2 parents 0ec34ac + 4d3eaf0 commit 7ca683c

File tree

3 files changed

+81
-4
lines changed

3 files changed

+81
-4
lines changed

clang/include/clang/Lex/HeaderSearch.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ struct HeaderFileInfo {
8484
/// `ModuleMap::isModular()`).
8585
unsigned isModuleHeader : 1;
8686

87-
/// Whether this header is a `textual header` in a module.
87+
/// Whether this header is a `textual header` in a module. If a header is
88+
/// textual in one module and normal in another module, this bit will not be
89+
/// set, only `isModuleHeader`.
8890
unsigned isTextualModuleHeader : 1;
8991

9092
/// Whether this header is part of the module that we are building, even if it

clang/lib/Lex/HeaderSearch.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,11 +1321,18 @@ OptionalFileEntryRef HeaderSearch::LookupSubframeworkHeader(
13211321
// File Info Management.
13221322
//===----------------------------------------------------------------------===//
13231323

1324+
static bool moduleMembershipNeedsMerge(const HeaderFileInfo *HFI,
1325+
ModuleMap::ModuleHeaderRole Role) {
1326+
if (ModuleMap::isModular(Role))
1327+
return !HFI->isModuleHeader || HFI->isTextualModuleHeader;
1328+
if (!HFI->isModuleHeader && (Role & ModuleMap::TextualHeader))
1329+
return !HFI->isTextualModuleHeader;
1330+
return false;
1331+
}
1332+
13241333
static void mergeHeaderFileInfoModuleBits(HeaderFileInfo &HFI,
13251334
bool isModuleHeader,
13261335
bool isTextualModuleHeader) {
1327-
assert((!isModuleHeader || !isTextualModuleHeader) &&
1328-
"A header can't build with a module and be textual at the same time");
13291336
HFI.isModuleHeader |= isModuleHeader;
13301337
if (HFI.isModuleHeader)
13311338
HFI.isTextualModuleHeader = false;
@@ -1440,7 +1447,7 @@ void HeaderSearch::MarkFileModuleHeader(const FileEntry *FE,
14401447
if ((Role & ModuleMap::ExcludedHeader))
14411448
return;
14421449
auto *HFI = getExistingFileInfo(FE);
1443-
if (HFI && HFI->isModuleHeader)
1450+
if (HFI && !moduleMembershipNeedsMerge(HFI, Role))
14441451
return;
14451452
}
14461453

clang/unittests/Lex/HeaderSearchTest.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,5 +292,73 @@ TEST_F(HeaderSearchTest, HeaderMapFrameworkLookup) {
292292
EXPECT_EQ(Search.getIncludeNameForHeader(FE), "Foo/Foo.h");
293293
}
294294

295+
TEST_F(HeaderSearchTest, HeaderFileInfoMerge) {
296+
auto AddHeader = [&](std::string HeaderPath) -> FileEntryRef {
297+
VFS->addFile(HeaderPath, 0,
298+
llvm::MemoryBuffer::getMemBufferCopy("", HeaderPath),
299+
/*User=*/std::nullopt, /*Group=*/std::nullopt,
300+
llvm::sys::fs::file_type::regular_file);
301+
return *FileMgr.getOptionalFileRef(HeaderPath);
302+
};
303+
304+
class MockExternalHeaderFileInfoSource : public ExternalHeaderFileInfoSource {
305+
HeaderFileInfo GetHeaderFileInfo(const FileEntry *FE) {
306+
HeaderFileInfo HFI;
307+
auto FileName = FE->getName();
308+
if (FileName == ModularPath)
309+
HFI.mergeModuleMembership(ModuleMap::NormalHeader);
310+
else if (FileName == TextualPath)
311+
HFI.mergeModuleMembership(ModuleMap::TextualHeader);
312+
HFI.External = true;
313+
HFI.IsValid = true;
314+
return HFI;
315+
}
316+
317+
public:
318+
std::string ModularPath = "/modular.h";
319+
std::string TextualPath = "/textual.h";
320+
};
321+
322+
auto ExternalSource = new MockExternalHeaderFileInfoSource();
323+
Search.SetExternalSource(ExternalSource);
324+
325+
// Everything should start out external.
326+
auto ModularFE = AddHeader(ExternalSource->ModularPath);
327+
auto TextualFE = AddHeader(ExternalSource->TextualPath);
328+
EXPECT_TRUE(Search.getExistingFileInfo(ModularFE)->External);
329+
EXPECT_TRUE(Search.getExistingFileInfo(TextualFE)->External);
330+
331+
// Marking the same role should keep it external
332+
Search.MarkFileModuleHeader(ModularFE, ModuleMap::NormalHeader,
333+
/*isCompilingModuleHeader=*/false);
334+
Search.MarkFileModuleHeader(TextualFE, ModuleMap::TextualHeader,
335+
/*isCompilingModuleHeader=*/false);
336+
EXPECT_TRUE(Search.getExistingFileInfo(ModularFE)->External);
337+
EXPECT_TRUE(Search.getExistingFileInfo(TextualFE)->External);
338+
339+
// textual -> modular should update the HFI, but modular -> textual should be
340+
// a no-op.
341+
Search.MarkFileModuleHeader(ModularFE, ModuleMap::TextualHeader,
342+
/*isCompilingModuleHeader=*/false);
343+
Search.MarkFileModuleHeader(TextualFE, ModuleMap::NormalHeader,
344+
/*isCompilingModuleHeader=*/false);
345+
auto ModularFI = Search.getExistingFileInfo(ModularFE);
346+
auto TextualFI = Search.getExistingFileInfo(TextualFE);
347+
EXPECT_TRUE(ModularFI->External);
348+
EXPECT_TRUE(ModularFI->isModuleHeader);
349+
EXPECT_FALSE(ModularFI->isTextualModuleHeader);
350+
EXPECT_FALSE(TextualFI->External);
351+
EXPECT_TRUE(TextualFI->isModuleHeader);
352+
EXPECT_FALSE(TextualFI->isTextualModuleHeader);
353+
354+
// Compiling the module should make the HFI local.
355+
Search.MarkFileModuleHeader(ModularFE, ModuleMap::NormalHeader,
356+
/*isCompilingModuleHeader=*/true);
357+
Search.MarkFileModuleHeader(TextualFE, ModuleMap::NormalHeader,
358+
/*isCompilingModuleHeader=*/true);
359+
EXPECT_FALSE(Search.getExistingFileInfo(ModularFE)->External);
360+
EXPECT_FALSE(Search.getExistingFileInfo(TextualFE)->External);
361+
}
362+
295363
} // namespace
296364
} // namespace clang

0 commit comments

Comments
 (0)