Skip to content

Commit 3cca522

Browse files
authored
[C++20] [Modules] Warn for duplicated decls in mutliple module units (#105799)
It is a long standing issue that the duplicated declarations in multiple module units would cause the compilation performance to get slowed down. And there are many questions or issue reports. So I think it is better to add a warning for it. And given this is not because the users' code violates the language specification or any best practices, the warning is disabled by default even if `-Wall` is specified. The users need to specify the warning explcitly or use `Weverything`. The documentation will add separately.
1 parent cf6cd1f commit 3cca522

File tree

5 files changed

+152
-3
lines changed

5 files changed

+152
-3
lines changed

clang/include/clang/Basic/DiagnosticSerializationKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ def warn_module_system_bit_conflict : Warning<
134134
"as a non-system module; any difference in diagnostic options will be ignored">,
135135
InGroup<ModuleConflict>;
136136

137+
def warn_decls_in_multiple_modules : Warning<
138+
"declaration %0 is detected to be defined in multiple module units, first is from '%1' and second is from '%2'; "
139+
"the compiler may not be good at merging the definitions. ">,
140+
InGroup<DiagGroup<"decls-in-multiple-modules">>,
141+
DefaultIgnore;
142+
137143
def err_failed_to_find_module_file : Error<
138144
"failed to find module file for module '%0'">;
139145
} // let CategoryName

clang/include/clang/Serialization/ASTReader.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,12 @@ class ASTReader
648648
/// performed deduplication.
649649
llvm::SetVector<NamedDecl *> PendingMergedDefinitionsToDeduplicate;
650650

651+
/// The duplicated definitions in module units which are pending to be warned.
652+
/// We need to delay it to wait for the loading of definitions since we don't
653+
/// want to warn for forward declarations.
654+
llvm::SmallVector<std::pair<Decl *, Decl *>>
655+
PendingWarningForDuplicatedDefsInModuleUnits;
656+
651657
/// Read the record that describes the lexical contents of a DC.
652658
bool ReadLexicalDeclContextStorage(ModuleFile &M,
653659
llvm::BitstreamCursor &Cursor,

clang/lib/Serialization/ASTReader.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9955,6 +9955,45 @@ void ASTReader::finishPendingActions() {
99559955
}
99569956
PendingDefinitions.clear();
99579957

9958+
for (auto [D, Previous] : PendingWarningForDuplicatedDefsInModuleUnits) {
9959+
auto hasDefinitionImpl = [this](Decl *D, auto hasDefinitionImpl) {
9960+
if (auto *VD = dyn_cast<VarDecl>(D))
9961+
return VD->isThisDeclarationADefinition() ||
9962+
VD->isThisDeclarationADemotedDefinition();
9963+
9964+
if (auto *TD = dyn_cast<TagDecl>(D))
9965+
return TD->isThisDeclarationADefinition() ||
9966+
TD->isThisDeclarationADemotedDefinition();
9967+
9968+
if (auto *FD = dyn_cast<FunctionDecl>(D))
9969+
return FD->isThisDeclarationADefinition() || PendingBodies.count(FD);
9970+
9971+
if (auto *RTD = dyn_cast<RedeclarableTemplateDecl>(D))
9972+
return hasDefinitionImpl(RTD->getTemplatedDecl(), hasDefinitionImpl);
9973+
9974+
// Conservatively return false here.
9975+
return false;
9976+
};
9977+
9978+
auto hasDefinition = [this, &hasDefinitionImpl](Decl *D) {
9979+
return hasDefinitionImpl(D, hasDefinitionImpl);
9980+
};
9981+
9982+
// It is not good to prevent multiple declarations since the forward
9983+
// declaration is common. Let's try to avoid duplicated definitions
9984+
// only.
9985+
if (!hasDefinition(D) || !hasDefinition(Previous))
9986+
continue;
9987+
9988+
Module *PM = Previous->getOwningModule();
9989+
Module *DM = D->getOwningModule();
9990+
Diag(D->getLocation(), diag::warn_decls_in_multiple_modules)
9991+
<< cast<NamedDecl>(Previous) << PM->getTopLevelModuleName()
9992+
<< (DM ? DM->getTopLevelModuleName() : "global module");
9993+
Diag(Previous->getLocation(), diag::note_also_found);
9994+
}
9995+
PendingWarningForDuplicatedDefsInModuleUnits.clear();
9996+
99589997
// Load the bodies of any functions or methods we've encountered. We do
99599998
// this now (delayed) so that we can be sure that the declaration chains
99609999
// have been fully wired up (hasBody relies on this).

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
320320
static void attachPreviousDecl(ASTReader &Reader, Decl *D, Decl *Previous,
321321
Decl *Canon);
322322

323+
static void checkMultipleDefinitionInNamedModules(ASTReader &Reader, Decl *D,
324+
Decl *Previous);
325+
323326
template <typename DeclT>
324327
static void attachLatestDeclImpl(Redeclarable<DeclT> *D, Decl *Latest);
325328
static void attachLatestDeclImpl(...);
@@ -3690,8 +3693,9 @@ static void inheritDefaultTemplateArguments(ASTContext &Context,
36903693
// [basic.link]/p10:
36913694
// If two declarations of an entity are attached to different modules,
36923695
// the program is ill-formed;
3693-
static void checkMultipleDefinitionInNamedModules(ASTReader &Reader, Decl *D,
3694-
Decl *Previous) {
3696+
void ASTDeclReader::checkMultipleDefinitionInNamedModules(ASTReader &Reader,
3697+
Decl *D,
3698+
Decl *Previous) {
36953699
// If it is previous implcitly introduced, it is not meaningful to
36963700
// diagnose it.
36973701
if (Previous->isImplicit())
@@ -3721,8 +3725,19 @@ static void checkMultipleDefinitionInNamedModules(ASTReader &Reader, Decl *D,
37213725
return;
37223726

37233727
// We only forbids merging decls within named modules.
3724-
if (!M->isNamedModule())
3728+
if (!M->isNamedModule()) {
3729+
// Try to warn the case that we merged decls from global module.
3730+
if (!M->isGlobalModule())
3731+
return;
3732+
3733+
if (D->getOwningModule() &&
3734+
M->getTopLevelModule() == D->getOwningModule()->getTopLevelModule())
3735+
return;
3736+
3737+
Reader.PendingWarningForDuplicatedDefsInModuleUnits.push_back(
3738+
{D, Previous});
37253739
return;
3740+
}
37263741

37273742
// It is fine if they are in the same module.
37283743
if (Reader.getContext().isInSameModule(M, D->getOwningModule()))
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang_cc1 -std=c++20 %t/m1.cppm -emit-module-interface -o %t/m1.pcm
6+
// RUN: %clang_cc1 -std=c++20 %t/m2.cppm -emit-module-interface -o %t/m2.pcm
7+
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cc -fsyntax-only \
8+
// RUN: -verify
9+
//
10+
// RUN: %clang_cc1 -std=c++20 %t/m1.cppm -Wall -emit-module-interface -o %t/m1.pcm
11+
// RUN: %clang_cc1 -std=c++20 %t/m2.cppm -Wall -emit-module-interface -o %t/m2.pcm
12+
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cc -fsyntax-only \
13+
// RUN: -verify -Wall
14+
//
15+
// RUN: %clang_cc1 -std=c++20 %t/m1.cppm -Wdecls-in-multiple-modules -emit-module-interface -o %t/m1.pcm
16+
// RUN: %clang_cc1 -std=c++20 %t/m2.cppm -Wdecls-in-multiple-modules -emit-module-interface -o %t/m2.pcm
17+
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cc -fsyntax-only \
18+
// RUN: -verify -Wdecls-in-multiple-modules -DWARNING
19+
20+
//--- foo.h
21+
#ifndef FOO_H
22+
#define FOO_H
23+
24+
enum E { E1, E2 };
25+
26+
int a = 43;
27+
28+
class foo {
29+
public:
30+
void consume(E, int);
31+
};
32+
33+
inline void func() {}
34+
35+
void fwd_decl();
36+
37+
#endif
38+
39+
//--- m1.cppm
40+
module;
41+
#include "foo.h"
42+
export module m1;
43+
export {
44+
using ::foo;
45+
using ::a;
46+
using ::func;
47+
using ::fwd_decl;
48+
using ::E;
49+
}
50+
51+
//--- m2.cppm
52+
module;
53+
#include "foo.h"
54+
export module m2;
55+
export {
56+
using ::foo;
57+
using ::a;
58+
using ::func;
59+
using ::fwd_decl;
60+
using ::E;
61+
}
62+
63+
//--- use.cc
64+
import m1;
65+
import m2;
66+
void use();
67+
void use() {
68+
E e = E1;
69+
foo f;
70+
f.consume(e, a);
71+
func();
72+
fwd_decl();
73+
}
74+
75+
#ifndef WARNING
76+
// expected-no-diagnostics
77+
#else
78+
// expected-warning@* {{declaration 'E' is detected to be defined in multiple module units}}
79+
// expected-warning@* {{declaration 'foo' is detected to be defined in multiple module units}}
80+
// expected-warning@* {{declaration 'a' is detected to be defined in multiple module units}}
81+
// expected-warning@* {{declaration 'func' is detected to be defined in multiple module units}}
82+
// expected-note@* 1+ {{}}
83+
#endif

0 commit comments

Comments
 (0)