Skip to content

Commit 94bfde4

Browse files
ChuanqiXu9llvmbot
authored andcommitted
[Serialization] Record whether the ODR is skipped (llvm#82302)
Close llvm#80570. In llvm@a0b6747, we skipped ODR checks for decls in GMF. Then it should be natural to skip storing the ODR values in BMI. Generally it should be fine as long as the writer and the reader keep consistent. However, the use of preamble in clangd shows the tricky part. For, ``` // test.cpp module; // any one off these is enough to crash clangd // #include <iostream> // #include <string_view> // #include <cmath> // #include <system_error> // #include <new> // #include <bit> // probably many more // only ok with libc++, not the system provided libstdc++ 13.2.1 // these are ok export module test; ``` clangd will store the headers as preamble to speedup the parsing and the preamble reuses the serialization techniques. (Generally we'd call the preamble as PCH. However it is not true strictly. I've tested the PCH wouldn't be problematic.) However, the tricky part is that the preamble is not modules. It literally serialiaze and deserialize things. So before clangd parsing the above test module, clangd will serialize the headers into the preamble. Note that there is no concept like GMF now. So the ODR bits are stored. However, when clangd parse the file actually, the decls from preamble are thought as in GMF literally, then hte ODR bits are skipped. Then mismatch happens. To solve the problem, this patch adds another bit for decls to record whether or not the ODR bits are skipped. (cherry picked from commit 49775b1)
1 parent c7b0a6e commit 94bfde4

File tree

5 files changed

+154
-10
lines changed

5 files changed

+154
-10
lines changed

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -800,11 +800,12 @@ void ASTDeclReader::VisitEnumDecl(EnumDecl *ED) {
800800
BitsUnpacker EnumDeclBits(Record.readInt());
801801
ED->setNumPositiveBits(EnumDeclBits.getNextBits(/*Width=*/8));
802802
ED->setNumNegativeBits(EnumDeclBits.getNextBits(/*Width=*/8));
803+
bool ShouldSkipCheckingODR = EnumDeclBits.getNextBit();
803804
ED->setScoped(EnumDeclBits.getNextBit());
804805
ED->setScopedUsingClassTag(EnumDeclBits.getNextBit());
805806
ED->setFixed(EnumDeclBits.getNextBit());
806807

807-
if (!shouldSkipCheckingODR(ED)) {
808+
if (!ShouldSkipCheckingODR) {
808809
ED->setHasODRHash(true);
809810
ED->ODRHash = Record.readInt();
810811
}
@@ -1073,6 +1074,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
10731074

10741075
FD->setCachedLinkage((Linkage)FunctionDeclBits.getNextBits(/*Width=*/3));
10751076
FD->setStorageClass((StorageClass)FunctionDeclBits.getNextBits(/*Width=*/3));
1077+
bool ShouldSkipCheckingODR = FunctionDeclBits.getNextBit();
10761078
FD->setInlineSpecified(FunctionDeclBits.getNextBit());
10771079
FD->setImplicitlyInline(FunctionDeclBits.getNextBit());
10781080
FD->setHasSkippedBody(FunctionDeclBits.getNextBit());
@@ -1102,7 +1104,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
11021104
if (FD->isExplicitlyDefaulted())
11031105
FD->setDefaultLoc(readSourceLocation());
11041106

1105-
if (!shouldSkipCheckingODR(FD)) {
1107+
if (!ShouldSkipCheckingODR) {
11061108
FD->ODRHash = Record.readInt();
11071109
FD->setHasODRHash(true);
11081110
}
@@ -1973,6 +1975,8 @@ void ASTDeclReader::ReadCXXDefinitionData(
19731975

19741976
BitsUnpacker CXXRecordDeclBits = Record.readInt();
19751977

1978+
bool ShouldSkipCheckingODR = CXXRecordDeclBits.getNextBit();
1979+
19761980
#define FIELD(Name, Width, Merge) \
19771981
if (!CXXRecordDeclBits.canGetNextNBits(Width)) \
19781982
CXXRecordDeclBits.updateValue(Record.readInt()); \
@@ -1982,7 +1986,7 @@ void ASTDeclReader::ReadCXXDefinitionData(
19821986
#undef FIELD
19831987

19841988
// We only perform ODR checks for decls not in GMF.
1985-
if (!shouldSkipCheckingODR(D)) {
1989+
if (!ShouldSkipCheckingODR) {
19861990
// Note: the caller has deserialized the IsLambda bit already.
19871991
Data.ODRHash = Record.readInt();
19881992
Data.HasODRHash = true;

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6010,6 +6010,9 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
60106010

60116011
BitsPacker DefinitionBits;
60126012

6013+
bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
6014+
DefinitionBits.addBit(ShouldSkipCheckingODR);
6015+
60136016
#define FIELD(Name, Width, Merge) \
60146017
if (!DefinitionBits.canWriteNextNBits(Width)) { \
60156018
Record->push_back(DefinitionBits); \
@@ -6023,11 +6026,10 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
60236026
Record->push_back(DefinitionBits);
60246027

60256028
// We only perform ODR checks for decls not in GMF.
6026-
if (!shouldSkipCheckingODR(D)) {
6029+
if (!ShouldSkipCheckingODR)
60276030
// getODRHash will compute the ODRHash if it has not been previously
60286031
// computed.
60296032
Record->push_back(D->getODRHash());
6030-
}
60316033

60326034
bool ModulesDebugInfo =
60336035
Writer->Context->getLangOpts().ModulesDebugInfo && !D->isDependentType();

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,13 +488,15 @@ void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) {
488488
BitsPacker EnumDeclBits;
489489
EnumDeclBits.addBits(D->getNumPositiveBits(), /*BitWidth=*/8);
490490
EnumDeclBits.addBits(D->getNumNegativeBits(), /*BitWidth=*/8);
491+
bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
492+
EnumDeclBits.addBit(ShouldSkipCheckingODR);
491493
EnumDeclBits.addBit(D->isScoped());
492494
EnumDeclBits.addBit(D->isScopedUsingClassTag());
493495
EnumDeclBits.addBit(D->isFixed());
494496
Record.push_back(EnumDeclBits);
495497

496498
// We only perform ODR checks for decls not in GMF.
497-
if (!shouldSkipCheckingODR(D))
499+
if (!ShouldSkipCheckingODR)
498500
Record.push_back(D->getODRHash());
499501

500502
if (MemberSpecializationInfo *MemberInfo = D->getMemberSpecializationInfo()) {
@@ -678,6 +680,8 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
678680
// FIXME: stable encoding
679681
FunctionDeclBits.addBits(llvm::to_underlying(D->getLinkageInternal()), 3);
680682
FunctionDeclBits.addBits((uint32_t)D->getStorageClass(), /*BitWidth=*/3);
683+
bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
684+
FunctionDeclBits.addBit(ShouldSkipCheckingODR);
681685
FunctionDeclBits.addBit(D->isInlineSpecified());
682686
FunctionDeclBits.addBit(D->isInlined());
683687
FunctionDeclBits.addBit(D->hasSkippedBody());
@@ -704,7 +708,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
704708
Record.AddSourceLocation(D->getDefaultLoc());
705709

706710
// We only perform ODR checks for decls not in GMF.
707-
if (!shouldSkipCheckingODR(D))
711+
if (!ShouldSkipCheckingODR)
708712
Record.push_back(D->getODRHash());
709713

710714
if (D->isDefaulted()) {
@@ -2137,12 +2141,13 @@ getFunctionDeclAbbrev(serialization::DeclCode Code) {
21372141
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 11)); // IDNS
21382142
Abv->Add(BitCodeAbbrevOp(
21392143
BitCodeAbbrevOp::Fixed,
2140-
27)); // Packed Function Bits: StorageClass, Inline, InlineSpecified,
2144+
28)); // Packed Function Bits: StorageClass, Inline, InlineSpecified,
21412145
// VirtualAsWritten, Pure, HasInheritedProto, HasWrittenProto,
21422146
// Deleted, Trivial, TrivialForCall, Defaulted, ExplicitlyDefaulted,
21432147
// IsIneligibleOrNotSelected, ImplicitReturnZero, Constexpr,
21442148
// UsesSEHTry, SkippedBody, MultiVersion, LateParsed,
2145-
// FriendConstraintRefersToEnclosingTemplate, Linkage
2149+
// FriendConstraintRefersToEnclosingTemplate, Linkage,
2150+
// ShouldSkipCheckingODR
21462151
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LocEnd
21472152
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // ODRHash
21482153
// This Array slurps the rest of the record. Fortunately we want to encode
@@ -2269,7 +2274,7 @@ void ASTWriter::WriteDeclAbbrevs() {
22692274
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // AddTypeRef
22702275
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // IntegerType
22712276
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // getPromotionType
2272-
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 19)); // Enum Decl Bits
2277+
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 20)); // Enum Decl Bits
22732278
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32));// ODRHash
22742279
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // InstantiatedMembEnum
22752280
// DC

clang/unittests/Serialization/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_clang_unittest(SerializationTests
1010
InMemoryModuleCacheTest.cpp
1111
ModuleCacheTest.cpp
1212
NoCommentsTest.cpp
13+
PreambleInNamedModulesTest.cpp
1314
SourceLocationEncodingTest.cpp
1415
VarDeclConstantInitTest.cpp
1516
)
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//===- unittests/Serialization/PreambleInNamedModulesTest.cpp -------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/Frontend/CompilerInstance.h"
10+
#include "clang/Frontend/CompilerInvocation.h"
11+
#include "clang/Frontend/FrontendActions.h"
12+
#include "clang/Frontend/PrecompiledPreamble.h"
13+
#include "llvm/ADT/SmallString.h"
14+
#include "llvm/Support/FileSystem.h"
15+
#include "llvm/Support/raw_ostream.h"
16+
17+
#include "gtest/gtest.h"
18+
19+
using namespace llvm;
20+
using namespace clang;
21+
22+
namespace {
23+
24+
class PreambleInNamedModulesTest : public ::testing::Test {
25+
void SetUp() override {
26+
ASSERT_FALSE(sys::fs::createUniqueDirectory("modules-test", TestDir));
27+
}
28+
29+
void TearDown() override { sys::fs::remove_directories(TestDir); }
30+
31+
public:
32+
using PathType = SmallString<256>;
33+
34+
PathType TestDir;
35+
36+
void addFile(StringRef Path, StringRef Contents, PathType &AbsPath) {
37+
ASSERT_FALSE(sys::path::is_absolute(Path));
38+
39+
AbsPath = TestDir;
40+
sys::path::append(AbsPath, Path);
41+
42+
ASSERT_FALSE(
43+
sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
44+
45+
std::error_code EC;
46+
llvm::raw_fd_ostream OS(AbsPath, EC);
47+
ASSERT_FALSE(EC);
48+
OS << Contents;
49+
}
50+
51+
void addFile(StringRef Path, StringRef Contents) {
52+
PathType UnusedAbsPath;
53+
addFile(Path, Contents, UnusedAbsPath);
54+
}
55+
};
56+
57+
// Testing that the use of Preamble in named modules can work basically.
58+
// See https://github.com/llvm/llvm-project/issues/80570
59+
TEST_F(PreambleInNamedModulesTest, BasicTest) {
60+
addFile("foo.h", R"cpp(
61+
enum class E {
62+
A,
63+
B,
64+
C,
65+
D
66+
};
67+
)cpp");
68+
69+
PathType MainFilePath;
70+
addFile("A.cppm", R"cpp(
71+
module;
72+
#include "foo.h"
73+
export module A;
74+
export using ::E;
75+
)cpp",
76+
MainFilePath);
77+
78+
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
79+
CompilerInstance::createDiagnostics(new DiagnosticOptions());
80+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
81+
llvm::vfs::createPhysicalFileSystem();
82+
83+
CreateInvocationOptions CIOpts;
84+
CIOpts.Diags = Diags;
85+
CIOpts.VFS = VFS;
86+
87+
const char *Args[] = {"clang++", "-std=c++20", "-working-directory",
88+
TestDir.c_str(), MainFilePath.c_str()};
89+
std::shared_ptr<CompilerInvocation> Invocation =
90+
createInvocation(Args, CIOpts);
91+
ASSERT_TRUE(Invocation);
92+
93+
llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> ContentsBuffer =
94+
llvm::MemoryBuffer::getFile(MainFilePath, /*IsText=*/true);
95+
EXPECT_TRUE(ContentsBuffer);
96+
std::unique_ptr<MemoryBuffer> Buffer = std::move(*ContentsBuffer);
97+
98+
PreambleBounds Bounds =
99+
ComputePreambleBounds(Invocation->getLangOpts(), *Buffer, 0);
100+
101+
PreambleCallbacks Callbacks;
102+
llvm::ErrorOr<PrecompiledPreamble> BuiltPreamble = PrecompiledPreamble::Build(
103+
*Invocation, Buffer.get(), Bounds, *Diags, VFS,
104+
std::make_shared<PCHContainerOperations>(),
105+
/*StoreInMemory=*/false, /*StoragePath=*/TestDir, Callbacks);
106+
107+
ASSERT_FALSE(Diags->hasErrorOccurred());
108+
109+
EXPECT_TRUE(BuiltPreamble);
110+
EXPECT_TRUE(BuiltPreamble->CanReuse(*Invocation, *Buffer, Bounds, *VFS));
111+
BuiltPreamble->OverridePreamble(*Invocation, VFS, Buffer.get());
112+
113+
auto Clang = std::make_unique<CompilerInstance>(
114+
std::make_shared<PCHContainerOperations>());
115+
Clang->setInvocation(std::move(Invocation));
116+
Clang->setDiagnostics(Diags.get());
117+
118+
if (auto VFSWithRemapping = createVFSFromCompilerInvocation(
119+
Clang->getInvocation(), Clang->getDiagnostics(), VFS))
120+
VFS = VFSWithRemapping;
121+
122+
Clang->createFileManager(VFS);
123+
EXPECT_TRUE(Clang->createTarget());
124+
125+
Buffer.release();
126+
127+
SyntaxOnlyAction Action;
128+
EXPECT_TRUE(Clang->ExecuteAction(Action));
129+
EXPECT_FALSE(Clang->getDiagnosticsPtr()->hasErrorOccurred());
130+
}
131+
132+
} // namespace

0 commit comments

Comments
 (0)