Skip to content

Commit 3f6bc1a

Browse files
committed
[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 099be86 commit 3f6bc1a

File tree

9 files changed

+93
-18
lines changed

9 files changed

+93
-18
lines changed

clang/include/clang/AST/DeclBase.h

Lines changed: 10 additions & 0 deletions
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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2456,13 +2456,6 @@ class BitsUnpacker {
24562456
uint32_t Value;
24572457
uint32_t CurrentBitsIndex = ~0;
24582458
};
2459-
2460-
inline bool shouldSkipCheckingODR(const Decl *D) {
2461-
return D->getOwningModule() &&
2462-
D->getASTContext().getLangOpts().SkipODRCheckInGMF &&
2463-
D->getOwningModule()->isExplicitGlobalModule();
2464-
}
2465-
24662459
} // namespace clang
24672460

24682461
#endif // LLVM_CLANG_SERIALIZATION_ASTREADER_H

clang/lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4496,7 +4496,7 @@ unsigned FunctionDecl::getODRHash() {
44964496
}
44974497

44984498
class ODRHash Hash;
4499-
Hash.AddFunctionDecl(this);
4499+
Hash.AddFunctionDecl(this, /*SkipBody=*/shouldSkipCheckingODR());
45004500
setHasODRHash(true);
45014501
ODRHash = Hash.CalculateHash();
45024502
return ODRHash;

clang/lib/AST/DeclBase.cpp

Lines changed: 5 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9762,7 +9762,7 @@ void ASTReader::finishPendingActions() {
97629762
!NonConstDefn->isLateTemplateParsed() &&
97639763
// We only perform ODR checks for decls not in the explicit
97649764
// global module fragment.
9765-
!shouldSkipCheckingODR(FD) &&
9765+
!FD->shouldSkipCheckingODR() &&
97669766
FD->getODRHash() != NonConstDefn->getODRHash()) {
97679767
if (!isa<CXXMethodDecl>(FD)) {
97689768
PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 4 additions & 4 deletions
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6060,7 +6060,7 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
60606060

60616061
BitsPacker DefinitionBits;
60626062

6063-
bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
6063+
bool ShouldSkipCheckingODR = D->shouldSkipCheckingODR();
60646064
DefinitionBits.addBit(ShouldSkipCheckingODR);
60656065

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

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) {
519519
BitsPacker EnumDeclBits;
520520
EnumDeclBits.addBits(D->getNumPositiveBits(), /*BitWidth=*/8);
521521
EnumDeclBits.addBits(D->getNumNegativeBits(), /*BitWidth=*/8);
522-
bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
522+
bool ShouldSkipCheckingODR = D->shouldSkipCheckingODR();
523523
EnumDeclBits.addBit(ShouldSkipCheckingODR);
524524
EnumDeclBits.addBit(D->isScoped());
525525
EnumDeclBits.addBit(D->isScopedUsingClassTag());
@@ -545,7 +545,7 @@ void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) {
545545
!D->isTopLevelDeclInObjCContainer() &&
546546
!CXXRecordDecl::classofKind(D->getKind()) &&
547547
!D->getIntegerTypeSourceInfo() && !D->getMemberSpecializationInfo() &&
548-
!needsAnonymousDeclarationNumber(D) && !shouldSkipCheckingODR(D) &&
548+
!needsAnonymousDeclarationNumber(D) && !D->shouldSkipCheckingODR() &&
549549
D->getDeclName().getNameKind() == DeclarationName::Identifier)
550550
AbbrevToUse = Writer.getDeclEnumAbbrev();
551551

@@ -711,7 +711,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
711711
// FIXME: stable encoding
712712
FunctionDeclBits.addBits(llvm::to_underlying(D->getLinkageInternal()), 3);
713713
FunctionDeclBits.addBits((uint32_t)D->getStorageClass(), /*BitWidth=*/3);
714-
bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
714+
bool ShouldSkipCheckingODR = D->shouldSkipCheckingODR();
715715
FunctionDeclBits.addBit(ShouldSkipCheckingODR);
716716
FunctionDeclBits.addBit(D->isInlineSpecified());
717717
FunctionDeclBits.addBit(D->isInlined());
@@ -1545,7 +1545,7 @@ void ASTDeclWriter::VisitCXXMethodDecl(CXXMethodDecl *D) {
15451545
D->getFirstDecl() == D->getMostRecentDecl() && !D->isInvalidDecl() &&
15461546
!D->hasAttrs() && !D->isTopLevelDeclInObjCContainer() &&
15471547
D->getDeclName().getNameKind() == DeclarationName::Identifier &&
1548-
!shouldSkipCheckingODR(D) && !D->hasExtInfo() &&
1548+
!D->shouldSkipCheckingODR() && !D->hasExtInfo() &&
15491549
!D->isExplicitlyDefaulted()) {
15501550
if (D->getTemplatedKind() == FunctionDecl::TK_NonTemplate ||
15511551
D->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate ||
Lines changed: 67 additions & 0 deletions
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)