Skip to content

Commit 0bf7ff1

Browse files
ChuanqiXu9tstellar
authored andcommitted
[C++20] [Moduls] Avoid computing odr hash for functions from comparing constraint expression
Previously we disabled to compute ODR hash for declarations from the global module fragment. However, we missed the case that the functions lives in the concept requiments (see the attached the test files for example). And the mismatch causes the potential crashment. Due to we will set the function body as lazy after we deserialize it and we will only take its body when needed. However, we don't allow to take the body during deserializing. So it is actually potentially problematic if we set the body as lazy first and computing the hash value of the function, which requires to deserialize its body. So we will meet a crash here. This patch tries to solve the issue by not taking the body of the function from GMF. Note that we can't skip comparing the constraint expression from the GMF directly since it is an key part of the function selecting and it may be the reason why we can't return 0 directly for `FunctionDecl::getODRHash()` from the GMF.
1 parent a2c93b3 commit 0bf7ff1

File tree

9 files changed

+93
-18
lines changed

9 files changed

+93
-18
lines changed

clang/include/clang/AST/DeclBase.h

+10
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,16 @@ class alignas(8) Decl {
673673
/// fragment. See [module.global.frag]p3,4 for details.
674674
bool isDiscardedInGlobalModuleFragment() const { return false; }
675675

676+
/// Check if we should skip checking ODRHash for declaration \param D.
677+
///
678+
/// The existing ODRHash mechanism seems to be not stable enough and
679+
/// the false positive ODR violation reports are annoying and we rarely see
680+
/// true ODR violation reports. Also we learned that MSVC disabled ODR checks
681+
/// for declarations in GMF. So we try to disable ODR checks in the GMF to
682+
/// get better user experiences before we make the ODR violation checks stable
683+
/// enough.
684+
bool shouldSkipCheckingODR() const;
685+
676686
/// Return true if this declaration has an attribute which acts as
677687
/// definition of the entity, such as 'alias' or 'ifunc'.
678688
bool hasDefiningAttr() const;

clang/include/clang/Serialization/ASTReader.h

-7
Original file line numberDiff line numberDiff line change
@@ -2451,13 +2451,6 @@ class BitsUnpacker {
24512451
uint32_t Value;
24522452
uint32_t CurrentBitsIndex = ~0;
24532453
};
2454-
2455-
inline bool shouldSkipCheckingODR(const Decl *D) {
2456-
return D->getOwningModule() &&
2457-
D->getASTContext().getLangOpts().SkipODRCheckInGMF &&
2458-
D->getOwningModule()->isExplicitGlobalModule();
2459-
}
2460-
24612454
} // namespace clang
24622455

24632456
#endif // LLVM_CLANG_SERIALIZATION_ASTREADER_H

clang/lib/AST/Decl.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -4476,7 +4476,7 @@ unsigned FunctionDecl::getODRHash() {
44764476
}
44774477

44784478
class ODRHash Hash;
4479-
Hash.AddFunctionDecl(this);
4479+
Hash.AddFunctionDecl(this, /*SkipBody=*/shouldSkipCheckingODR());
44804480
setHasODRHash(true);
44814481
ODRHash = Hash.CalculateHash();
44824482
return ODRHash;

clang/lib/AST/DeclBase.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,11 @@ bool Decl::isInAnotherModuleUnit() const {
11021102
return M != getASTContext().getCurrentNamedModule();
11031103
}
11041104

1105+
bool Decl::shouldSkipCheckingODR() const {
1106+
return getASTContext().getLangOpts().SkipODRCheckInGMF && getOwningModule() &&
1107+
getOwningModule()->isExplicitGlobalModule();
1108+
}
1109+
11051110
static Decl::Kind getKind(const Decl *D) { return D->getKind(); }
11061111
static Decl::Kind getKind(const DeclContext *DC) { return DC->getDeclKind(); }
11071112

clang/lib/Serialization/ASTReader.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -9745,7 +9745,7 @@ void ASTReader::finishPendingActions() {
97459745
!NonConstDefn->isLateTemplateParsed() &&
97469746
// We only perform ODR checks for decls not in the explicit
97479747
// global module fragment.
9748-
!shouldSkipCheckingODR(FD) &&
9748+
!FD->shouldSkipCheckingODR() &&
97499749
FD->getODRHash() != NonConstDefn->getODRHash()) {
97509750
if (!isa<CXXMethodDecl>(FD)) {
97519751
PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);

clang/lib/Serialization/ASTReaderDecl.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,7 @@ void ASTDeclReader::VisitEnumDecl(EnumDecl *ED) {
832832
Reader.mergeDefinitionVisibility(OldDef, ED);
833833
// We don't want to check the ODR hash value for declarations from global
834834
// module fragment.
835-
if (!shouldSkipCheckingODR(ED) &&
835+
if (!ED->shouldSkipCheckingODR() &&
836836
OldDef->getODRHash() != ED->getODRHash())
837837
Reader.PendingEnumOdrMergeFailures[OldDef].push_back(ED);
838838
} else {
@@ -874,7 +874,7 @@ void ASTDeclReader::VisitRecordDecl(RecordDecl *RD) {
874874
VisitRecordDeclImpl(RD);
875875
// We should only reach here if we're in C/Objective-C. There is no
876876
// global module fragment.
877-
assert(!shouldSkipCheckingODR(RD));
877+
assert(!RD->shouldSkipCheckingODR());
878878
RD->setODRHash(Record.readInt());
879879

880880
// Maintain the invariant of a redeclaration chain containing only
@@ -2152,7 +2152,7 @@ void ASTDeclReader::MergeDefinitionData(
21522152
}
21532153

21542154
// We don't want to check ODR for decls in the global module fragment.
2155-
if (shouldSkipCheckingODR(MergeDD.Definition))
2155+
if (MergeDD.Definition->shouldSkipCheckingODR())
21562156
return;
21572157

21582158
if (D->getODRHash() != MergeDD.ODRHash) {
@@ -3526,7 +3526,7 @@ ASTDeclReader::FindExistingResult ASTDeclReader::findExisting(NamedDecl *D) {
35263526
// same template specialization into the same CXXRecordDecl.
35273527
auto MergedDCIt = Reader.MergedDeclContexts.find(D->getLexicalDeclContext());
35283528
if (MergedDCIt != Reader.MergedDeclContexts.end() &&
3529-
!shouldSkipCheckingODR(D) && MergedDCIt->second == D->getDeclContext())
3529+
!D->shouldSkipCheckingODR() && MergedDCIt->second == D->getDeclContext())
35303530
Reader.PendingOdrMergeChecks.push_back(D);
35313531

35323532
return FindExistingResult(Reader, D, /*Existing=*/nullptr,

clang/lib/Serialization/ASTWriter.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -6010,7 +6010,7 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
60106010

60116011
BitsPacker DefinitionBits;
60126012

6013-
bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
6013+
bool ShouldSkipCheckingODR = D->shouldSkipCheckingODR();
60146014
DefinitionBits.addBit(ShouldSkipCheckingODR);
60156015

60166016
#define FIELD(Name, Width, Merge) \

clang/lib/Serialization/ASTWriterDecl.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ 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);
491+
bool ShouldSkipCheckingODR = D->shouldSkipCheckingODR();
492492
EnumDeclBits.addBit(ShouldSkipCheckingODR);
493493
EnumDeclBits.addBit(D->isScoped());
494494
EnumDeclBits.addBit(D->isScopedUsingClassTag());
@@ -514,7 +514,7 @@ void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) {
514514
!D->isTopLevelDeclInObjCContainer() &&
515515
!CXXRecordDecl::classofKind(D->getKind()) &&
516516
!D->getIntegerTypeSourceInfo() && !D->getMemberSpecializationInfo() &&
517-
!needsAnonymousDeclarationNumber(D) && !shouldSkipCheckingODR(D) &&
517+
!needsAnonymousDeclarationNumber(D) && !D->shouldSkipCheckingODR() &&
518518
D->getDeclName().getNameKind() == DeclarationName::Identifier)
519519
AbbrevToUse = Writer.getDeclEnumAbbrev();
520520

@@ -680,7 +680,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
680680
// FIXME: stable encoding
681681
FunctionDeclBits.addBits(llvm::to_underlying(D->getLinkageInternal()), 3);
682682
FunctionDeclBits.addBits((uint32_t)D->getStorageClass(), /*BitWidth=*/3);
683-
bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
683+
bool ShouldSkipCheckingODR = D->shouldSkipCheckingODR();
684684
FunctionDeclBits.addBit(ShouldSkipCheckingODR);
685685
FunctionDeclBits.addBit(D->isInlineSpecified());
686686
FunctionDeclBits.addBit(D->isInlined());
@@ -1514,7 +1514,7 @@ void ASTDeclWriter::VisitCXXMethodDecl(CXXMethodDecl *D) {
15141514
D->getFirstDecl() == D->getMostRecentDecl() && !D->isInvalidDecl() &&
15151515
!D->hasAttrs() && !D->isTopLevelDeclInObjCContainer() &&
15161516
D->getDeclName().getNameKind() == DeclarationName::Identifier &&
1517-
!shouldSkipCheckingODR(D) && !D->hasExtInfo() &&
1517+
!D->shouldSkipCheckingODR() && !D->hasExtInfo() &&
15181518
!D->isExplicitlyDefaulted()) {
15191519
if (D->getTemplatedKind() == FunctionDecl::TK_NonTemplate ||
15201520
D->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate ||
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/A.cppm -emit-module-interface -o %t/A.pcm
6+
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/B.cppm -emit-module-interface -o %t/B.pcm
7+
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/test.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
8+
9+
//--- header.h
10+
#pragma once
11+
template <class _Tp>
12+
class Optional {};
13+
14+
template <class _Tp>
15+
concept C = requires(const _Tp& __t) {
16+
[]<class _Up>(const Optional<_Up>&) {}(__t);
17+
};
18+
19+
//--- func.h
20+
#include "header.h"
21+
template <C T>
22+
void func() {}
23+
24+
//--- duplicated_func.h
25+
#include "header.h"
26+
template <C T>
27+
void duplicated_func() {}
28+
29+
//--- test_func.h
30+
#include "func.h"
31+
32+
void test_func() {
33+
func<Optional<int>>();
34+
}
35+
36+
//--- test_duplicated_func.h
37+
#include "duplicated_func.h"
38+
39+
void test_duplicated_func() {
40+
duplicated_func<Optional<int>>();
41+
}
42+
43+
//--- A.cppm
44+
module;
45+
#include "header.h"
46+
#include "test_duplicated_func.h"
47+
export module A;
48+
export using ::test_duplicated_func;
49+
50+
//--- B.cppm
51+
module;
52+
#include "header.h"
53+
#include "test_func.h"
54+
#include "test_duplicated_func.h"
55+
export module B;
56+
export using ::test_func;
57+
export using ::test_duplicated_func;
58+
59+
//--- test.cpp
60+
// expected-no-diagnostics
61+
import A;
62+
import B;
63+
64+
void test() {
65+
test_func();
66+
test_duplicated_func();
67+
}

0 commit comments

Comments
 (0)