Skip to content

Commit a0b6747

Browse files
committed
[C++20] [Modules] Don't perform ODR checks in GMF
Close #79240. See the linked issue for details. Given the frequency of issue reporting about false positive ODR checks (I received private issue reports too), I'd like to backport this to 18.x too.
1 parent 0fbaf03 commit a0b6747

File tree

10 files changed

+68
-83
lines changed

10 files changed

+68
-83
lines changed

clang/docs/ReleaseNotes.rst

+5
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ C++ Language Changes
6262
C++20 Feature Support
6363
^^^^^^^^^^^^^^^^^^^^^
6464

65+
- Clang won't perform ODR checks for decls in the global module fragment any
66+
more to ease the implementation and improve the user's using experience.
67+
This follows the MSVC's behavior.
68+
(`#79240 <https://github.com/llvm/llvm-project/issues/79240>`_).
69+
6570
C++23 Feature Support
6671
^^^^^^^^^^^^^^^^^^^^^
6772

clang/include/clang/Serialization/ASTReader.h

+4
Original file line numberDiff line numberDiff line change
@@ -2452,6 +2452,10 @@ class BitsUnpacker {
24522452
uint32_t CurrentBitsIndex = ~0;
24532453
};
24542454

2455+
inline bool isFromExplicitGMF(const Decl *D) {
2456+
return D->getOwningModule() && D->getOwningModule()->isExplicitGlobalModule();
2457+
}
2458+
24552459
} // namespace clang
24562460

24572461
#endif // LLVM_CLANG_SERIALIZATION_ASTREADER_H

clang/lib/Serialization/ASTReader.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -9747,6 +9747,9 @@ void ASTReader::finishPendingActions() {
97479747

97489748
if (!FD->isLateTemplateParsed() &&
97499749
!NonConstDefn->isLateTemplateParsed() &&
9750+
// We only perform ODR checks for decls not in the explicit
9751+
// global module fragment.
9752+
!isFromExplicitGMF(FD) &&
97509753
FD->getODRHash() != NonConstDefn->getODRHash()) {
97519754
if (!isa<CXXMethodDecl>(FD)) {
97529755
PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);

clang/lib/Serialization/ASTReaderDecl.cpp

+28-9
Original file line numberDiff line numberDiff line change
@@ -804,8 +804,10 @@ void ASTDeclReader::VisitEnumDecl(EnumDecl *ED) {
804804
ED->setScopedUsingClassTag(EnumDeclBits.getNextBit());
805805
ED->setFixed(EnumDeclBits.getNextBit());
806806

807-
ED->setHasODRHash(true);
808-
ED->ODRHash = Record.readInt();
807+
if (!isFromExplicitGMF(ED)) {
808+
ED->setHasODRHash(true);
809+
ED->ODRHash = Record.readInt();
810+
}
809811

810812
// If this is a definition subject to the ODR, and we already have a
811813
// definition, merge this one into it.
@@ -827,7 +829,9 @@ void ASTDeclReader::VisitEnumDecl(EnumDecl *ED) {
827829
Reader.MergedDeclContexts.insert(std::make_pair(ED, OldDef));
828830
ED->demoteThisDefinitionToDeclaration();
829831
Reader.mergeDefinitionVisibility(OldDef, ED);
830-
if (OldDef->getODRHash() != ED->getODRHash())
832+
// We don't want to check the ODR hash value for declarations from global
833+
// module fragment.
834+
if (!isFromExplicitGMF(ED) && OldDef->getODRHash() != ED->getODRHash())
831835
Reader.PendingEnumOdrMergeFailures[OldDef].push_back(ED);
832836
} else {
833837
OldDef = ED;
@@ -866,6 +870,9 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) {
866870

867871
void ASTDeclReader::VisitRecordDecl(RecordDecl *RD) {
868872
VisitRecordDeclImpl(RD);
873+
// We should only reach here if we're in C/Objective-C. There is no
874+
// global module fragment.
875+
assert(!isFromExplicitGMF(RD));
869876
RD->setODRHash(Record.readInt());
870877

871878
// Maintain the invariant of a redeclaration chain containing only
@@ -1094,8 +1101,10 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
10941101
if (FD->isExplicitlyDefaulted())
10951102
FD->setDefaultLoc(readSourceLocation());
10961103

1097-
FD->ODRHash = Record.readInt();
1098-
FD->setHasODRHash(true);
1104+
if (!isFromExplicitGMF(FD)) {
1105+
FD->ODRHash = Record.readInt();
1106+
FD->setHasODRHash(true);
1107+
}
10991108

11001109
if (FD->isDefaulted()) {
11011110
if (unsigned NumLookups = Record.readInt()) {
@@ -1971,9 +1980,12 @@ void ASTDeclReader::ReadCXXDefinitionData(
19711980
#include "clang/AST/CXXRecordDeclDefinitionBits.def"
19721981
#undef FIELD
19731982

1974-
// Note: the caller has deserialized the IsLambda bit already.
1975-
Data.ODRHash = Record.readInt();
1976-
Data.HasODRHash = true;
1983+
// We only perform ODR checks for decls not in GMF.
1984+
if (!isFromExplicitGMF(D)) {
1985+
// Note: the caller has deserialized the IsLambda bit already.
1986+
Data.ODRHash = Record.readInt();
1987+
Data.HasODRHash = true;
1988+
}
19771989

19781990
if (Record.readInt()) {
19791991
Reader.DefinitionSource[D] =
@@ -2134,6 +2146,10 @@ void ASTDeclReader::MergeDefinitionData(
21342146
}
21352147
}
21362148

2149+
// We don't want to check ODR for decls in the global module fragment.
2150+
if (isFromExplicitGMF(MergeDD.Definition))
2151+
return;
2152+
21372153
if (D->getODRHash() != MergeDD.ODRHash) {
21382154
DetectedOdrViolation = true;
21392155
}
@@ -3498,10 +3514,13 @@ ASTDeclReader::FindExistingResult ASTDeclReader::findExisting(NamedDecl *D) {
34983514
// If this declaration is from a merged context, make a note that we need to
34993515
// check that the canonical definition of that context contains the decl.
35003516
//
3517+
// Note that we don't perform ODR checks for decls from the global module
3518+
// fragment.
3519+
//
35013520
// FIXME: We should do something similar if we merge two definitions of the
35023521
// same template specialization into the same CXXRecordDecl.
35033522
auto MergedDCIt = Reader.MergedDeclContexts.find(D->getLexicalDeclContext());
3504-
if (MergedDCIt != Reader.MergedDeclContexts.end() &&
3523+
if (MergedDCIt != Reader.MergedDeclContexts.end() && !isFromExplicitGMF(D) &&
35053524
MergedDCIt->second == D->getDeclContext())
35063525
Reader.PendingOdrMergeChecks.push_back(D);
35073526

clang/lib/Serialization/ASTWriter.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -6027,8 +6027,12 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
60276027

60286028
Record->push_back(DefinitionBits);
60296029

6030-
// getODRHash will compute the ODRHash if it has not been previously computed.
6031-
Record->push_back(D->getODRHash());
6030+
// We only perform ODR checks for decls not in GMF.
6031+
if (!isFromExplicitGMF(D)) {
6032+
// getODRHash will compute the ODRHash if it has not been previously
6033+
// computed.
6034+
Record->push_back(D->getODRHash());
6035+
}
60326036

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

clang/lib/Serialization/ASTWriterDecl.cpp

+9-4
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,9 @@ void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) {
493493
EnumDeclBits.addBit(D->isFixed());
494494
Record.push_back(EnumDeclBits);
495495

496-
Record.push_back(D->getODRHash());
496+
// We only perform ODR checks for decls not in GMF.
497+
if (!isFromExplicitGMF(D))
498+
Record.push_back(D->getODRHash());
497499

498500
if (MemberSpecializationInfo *MemberInfo = D->getMemberSpecializationInfo()) {
499501
Record.AddDeclRef(MemberInfo->getInstantiatedFrom());
@@ -510,7 +512,7 @@ void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) {
510512
!D->isTopLevelDeclInObjCContainer() &&
511513
!CXXRecordDecl::classofKind(D->getKind()) &&
512514
!D->getIntegerTypeSourceInfo() && !D->getMemberSpecializationInfo() &&
513-
!needsAnonymousDeclarationNumber(D) &&
515+
!needsAnonymousDeclarationNumber(D) && !isFromExplicitGMF(D) &&
514516
D->getDeclName().getNameKind() == DeclarationName::Identifier)
515517
AbbrevToUse = Writer.getDeclEnumAbbrev();
516518

@@ -701,7 +703,9 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
701703
if (D->isExplicitlyDefaulted())
702704
Record.AddSourceLocation(D->getDefaultLoc());
703705

704-
Record.push_back(D->getODRHash());
706+
// We only perform ODR checks for decls not in GMF.
707+
if (!isFromExplicitGMF(D))
708+
Record.push_back(D->getODRHash());
705709

706710
if (D->isDefaulted()) {
707711
if (auto *FDI = D->getDefaultedFunctionInfo()) {
@@ -1506,7 +1510,8 @@ void ASTDeclWriter::VisitCXXMethodDecl(CXXMethodDecl *D) {
15061510
D->getFirstDecl() == D->getMostRecentDecl() && !D->isInvalidDecl() &&
15071511
!D->hasAttrs() && !D->isTopLevelDeclInObjCContainer() &&
15081512
D->getDeclName().getNameKind() == DeclarationName::Identifier &&
1509-
!D->hasExtInfo() && !D->isExplicitlyDefaulted()) {
1513+
!isFromExplicitGMF(D) && !D->hasExtInfo() &&
1514+
!D->isExplicitlyDefaulted()) {
15101515
if (D->getTemplatedKind() == FunctionDecl::TK_NonTemplate ||
15111516
D->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate ||
15121517
D->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization ||

clang/test/Modules/concept.cppm

+7-7
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,6 @@ module;
7070
export module B;
7171
import A;
7272

73-
#ifdef DIFFERENT
74-
// expected-error@foo.h:41 {{'__fn::operator()' from module 'A.<global>' is not present in definition of '__fn' provided earlier}}
75-
// expected-note@* 1+{{declaration of 'operator()' does not match}}
76-
#else
77-
// expected-no-diagnostics
78-
#endif
79-
8073
template <class T>
8174
struct U {
8275
auto operator+(U) { return 0; }
@@ -94,3 +87,10 @@ void foo() {
9487

9588
__fn{}(U<int>(), U<int>());
9689
}
90+
91+
#ifdef DIFFERENT
92+
// expected-error@B.cppm:* {{call to object of type '__fn' is ambiguous}}
93+
// expected-note@* 1+{{candidate function}}
94+
#else
95+
// expected-no-diagnostics
96+
#endif

clang/test/Modules/no-eager-load.cppm

-53
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,10 @@
99
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/d.cpp \
1010
// RUN: -fprebuilt-module-path=%t
1111
//
12-
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/e.cppm -o %t/e.pcm
13-
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/f.cppm -o %t/f.pcm
14-
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/g.cpp \
15-
// RUN: -fprebuilt-module-path=%t
16-
//
1712
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/h.cppm \
1813
// RUN: -fprebuilt-module-path=%t -o %t/h.pcm
19-
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/i.cppm \
20-
// RUN: -fprebuilt-module-path=%t -o %t/i.pcm
2114
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/j.cpp \
2215
// RUN: -fprebuilt-module-path=%t
23-
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/k.cpp \
24-
// RUN: -fprebuilt-module-path=%t
2516

2617
//--- a.cppm
2718
export module a;
@@ -53,58 +44,14 @@ void use() {
5344
// expected-note@* {{but in 'a' found a different body}}
5445
}
5546

56-
//--- foo.h
57-
void foo() {
58-
59-
}
60-
61-
//--- bar.h
62-
void bar();
63-
void foo() {
64-
bar();
65-
}
66-
67-
//--- e.cppm
68-
module;
69-
#include "foo.h"
70-
export module e;
71-
export using ::foo;
72-
73-
//--- f.cppm
74-
module;
75-
#include "bar.h"
76-
export module f;
77-
export using ::foo;
78-
79-
//--- g.cpp
80-
import e;
81-
import f;
82-
void use() {
83-
foo(); // expected-error@* {{'foo' has different definitions in different modules;}}
84-
// expected-note@* {{but in 'e.<global>' found a different body}}
85-
}
86-
8747
//--- h.cppm
8848
export module h;
8949
export import a;
9050
export import b;
9151

92-
//--- i.cppm
93-
export module i;
94-
export import e;
95-
export import f;
96-
9752
//--- j.cpp
9853
import h;
9954
void use() {
10055
foo(); // expected-error@* {{'foo' has different definitions in different modules;}}
10156
// expected-note@* {{but in 'a' found a different body}}
10257
}
103-
104-
//--- k.cpp
105-
import i;
106-
void use() {
107-
foo(); // expected-error@* {{'foo' has different definitions in different modules;}}
108-
// expected-note@* {{but in 'e.<global>' found a different body}}
109-
}
110-

clang/test/Modules/polluted-operator.cppm

+3-5
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,10 @@ module;
4646
export module a;
4747

4848
//--- b.cppm
49+
// This is actually an ODR violation. But given https://github.com/llvm/llvm-project/issues/79240,
50+
// we don't count it as an ODR violation any more.
51+
// expected-no-diagnostics
4952
module;
5053
#include "bar.h"
5154
export module b;
5255
import a;
53-
54-
// expected-error@* {{has different definitions in different modules; first difference is defined here found data member '_S_copy_ctor' with an initializer}}
55-
// expected-note@* {{but in 'a.<global>' found data member '_S_copy_ctor' with a different initializer}}
56-
// expected-error@* {{from module 'a.<global>' is not present in definition of 'variant<_Types...>' provided earlier}}
57-
// expected-note@* {{declaration of 'swap' does not match}}

clang/test/Modules/pr76638.cppm

+3-3
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,13 @@ export module mod3;
5757
export using std::align_val_t;
5858

5959
//--- mod4.cppm
60+
// This is actually an ODR violation. But given https://github.com/llvm/llvm-project/issues/79240,
61+
// we don't count it as an ODR violation now.
62+
// expected-no-diagnostics
6063
module;
6164
#include "signed_size_t.h"
6265
#include "csize_t"
6366
#include "align.h"
6467
export module mod4;
6568
import mod3;
6669
export using std::align_val_t;
67-
68-
// expected-error@align.h:* {{'std::align_val_t' has different definitions in different modules; defined here first difference is enum with specified type 'size_t' (aka 'int')}}
69-
// expected-note@align.h:* {{but in 'mod3.<global>' found enum with specified type 'size_t' (aka 'unsigned int')}}

0 commit comments

Comments
 (0)