[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.
This commit is contained in:
parent
cf6cd1fd67
commit
3cca522d21
@ -134,6 +134,12 @@ def warn_module_system_bit_conflict : Warning<
|
|||||||
"as a non-system module; any difference in diagnostic options will be ignored">,
|
"as a non-system module; any difference in diagnostic options will be ignored">,
|
||||||
InGroup<ModuleConflict>;
|
InGroup<ModuleConflict>;
|
||||||
|
|
||||||
|
def warn_decls_in_multiple_modules : Warning<
|
||||||
|
"declaration %0 is detected to be defined in multiple module units, first is from '%1' and second is from '%2'; "
|
||||||
|
"the compiler may not be good at merging the definitions. ">,
|
||||||
|
InGroup<DiagGroup<"decls-in-multiple-modules">>,
|
||||||
|
DefaultIgnore;
|
||||||
|
|
||||||
def err_failed_to_find_module_file : Error<
|
def err_failed_to_find_module_file : Error<
|
||||||
"failed to find module file for module '%0'">;
|
"failed to find module file for module '%0'">;
|
||||||
} // let CategoryName
|
} // let CategoryName
|
||||||
|
@ -648,6 +648,12 @@ private:
|
|||||||
/// performed deduplication.
|
/// performed deduplication.
|
||||||
llvm::SetVector<NamedDecl *> PendingMergedDefinitionsToDeduplicate;
|
llvm::SetVector<NamedDecl *> PendingMergedDefinitionsToDeduplicate;
|
||||||
|
|
||||||
|
/// The duplicated definitions in module units which are pending to be warned.
|
||||||
|
/// We need to delay it to wait for the loading of definitions since we don't
|
||||||
|
/// want to warn for forward declarations.
|
||||||
|
llvm::SmallVector<std::pair<Decl *, Decl *>>
|
||||||
|
PendingWarningForDuplicatedDefsInModuleUnits;
|
||||||
|
|
||||||
/// Read the record that describes the lexical contents of a DC.
|
/// Read the record that describes the lexical contents of a DC.
|
||||||
bool ReadLexicalDeclContextStorage(ModuleFile &M,
|
bool ReadLexicalDeclContextStorage(ModuleFile &M,
|
||||||
llvm::BitstreamCursor &Cursor,
|
llvm::BitstreamCursor &Cursor,
|
||||||
|
@ -9955,6 +9955,45 @@ void ASTReader::finishPendingActions() {
|
|||||||
}
|
}
|
||||||
PendingDefinitions.clear();
|
PendingDefinitions.clear();
|
||||||
|
|
||||||
|
for (auto [D, Previous] : PendingWarningForDuplicatedDefsInModuleUnits) {
|
||||||
|
auto hasDefinitionImpl = [this](Decl *D, auto hasDefinitionImpl) {
|
||||||
|
if (auto *VD = dyn_cast<VarDecl>(D))
|
||||||
|
return VD->isThisDeclarationADefinition() ||
|
||||||
|
VD->isThisDeclarationADemotedDefinition();
|
||||||
|
|
||||||
|
if (auto *TD = dyn_cast<TagDecl>(D))
|
||||||
|
return TD->isThisDeclarationADefinition() ||
|
||||||
|
TD->isThisDeclarationADemotedDefinition();
|
||||||
|
|
||||||
|
if (auto *FD = dyn_cast<FunctionDecl>(D))
|
||||||
|
return FD->isThisDeclarationADefinition() || PendingBodies.count(FD);
|
||||||
|
|
||||||
|
if (auto *RTD = dyn_cast<RedeclarableTemplateDecl>(D))
|
||||||
|
return hasDefinitionImpl(RTD->getTemplatedDecl(), hasDefinitionImpl);
|
||||||
|
|
||||||
|
// Conservatively return false here.
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto hasDefinition = [this, &hasDefinitionImpl](Decl *D) {
|
||||||
|
return hasDefinitionImpl(D, hasDefinitionImpl);
|
||||||
|
};
|
||||||
|
|
||||||
|
// It is not good to prevent multiple declarations since the forward
|
||||||
|
// declaration is common. Let's try to avoid duplicated definitions
|
||||||
|
// only.
|
||||||
|
if (!hasDefinition(D) || !hasDefinition(Previous))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Module *PM = Previous->getOwningModule();
|
||||||
|
Module *DM = D->getOwningModule();
|
||||||
|
Diag(D->getLocation(), diag::warn_decls_in_multiple_modules)
|
||||||
|
<< cast<NamedDecl>(Previous) << PM->getTopLevelModuleName()
|
||||||
|
<< (DM ? DM->getTopLevelModuleName() : "global module");
|
||||||
|
Diag(Previous->getLocation(), diag::note_also_found);
|
||||||
|
}
|
||||||
|
PendingWarningForDuplicatedDefsInModuleUnits.clear();
|
||||||
|
|
||||||
// Load the bodies of any functions or methods we've encountered. We do
|
// Load the bodies of any functions or methods we've encountered. We do
|
||||||
// this now (delayed) so that we can be sure that the declaration chains
|
// this now (delayed) so that we can be sure that the declaration chains
|
||||||
// have been fully wired up (hasBody relies on this).
|
// have been fully wired up (hasBody relies on this).
|
||||||
|
@ -320,6 +320,9 @@ public:
|
|||||||
static void attachPreviousDecl(ASTReader &Reader, Decl *D, Decl *Previous,
|
static void attachPreviousDecl(ASTReader &Reader, Decl *D, Decl *Previous,
|
||||||
Decl *Canon);
|
Decl *Canon);
|
||||||
|
|
||||||
|
static void checkMultipleDefinitionInNamedModules(ASTReader &Reader, Decl *D,
|
||||||
|
Decl *Previous);
|
||||||
|
|
||||||
template <typename DeclT>
|
template <typename DeclT>
|
||||||
static void attachLatestDeclImpl(Redeclarable<DeclT> *D, Decl *Latest);
|
static void attachLatestDeclImpl(Redeclarable<DeclT> *D, Decl *Latest);
|
||||||
static void attachLatestDeclImpl(...);
|
static void attachLatestDeclImpl(...);
|
||||||
@ -3690,8 +3693,9 @@ static void inheritDefaultTemplateArguments(ASTContext &Context,
|
|||||||
// [basic.link]/p10:
|
// [basic.link]/p10:
|
||||||
// If two declarations of an entity are attached to different modules,
|
// If two declarations of an entity are attached to different modules,
|
||||||
// the program is ill-formed;
|
// the program is ill-formed;
|
||||||
static void checkMultipleDefinitionInNamedModules(ASTReader &Reader, Decl *D,
|
void ASTDeclReader::checkMultipleDefinitionInNamedModules(ASTReader &Reader,
|
||||||
Decl *Previous) {
|
Decl *D,
|
||||||
|
Decl *Previous) {
|
||||||
// If it is previous implcitly introduced, it is not meaningful to
|
// If it is previous implcitly introduced, it is not meaningful to
|
||||||
// diagnose it.
|
// diagnose it.
|
||||||
if (Previous->isImplicit())
|
if (Previous->isImplicit())
|
||||||
@ -3721,8 +3725,19 @@ static void checkMultipleDefinitionInNamedModules(ASTReader &Reader, Decl *D,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// We only forbids merging decls within named modules.
|
// We only forbids merging decls within named modules.
|
||||||
if (!M->isNamedModule())
|
if (!M->isNamedModule()) {
|
||||||
|
// Try to warn the case that we merged decls from global module.
|
||||||
|
if (!M->isGlobalModule())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (D->getOwningModule() &&
|
||||||
|
M->getTopLevelModule() == D->getOwningModule()->getTopLevelModule())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Reader.PendingWarningForDuplicatedDefsInModuleUnits.push_back(
|
||||||
|
{D, Previous});
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// It is fine if they are in the same module.
|
// It is fine if they are in the same module.
|
||||||
if (Reader.getContext().isInSameModule(M, D->getOwningModule()))
|
if (Reader.getContext().isInSameModule(M, D->getOwningModule()))
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
// RUN: rm -rf %t
|
||||||
|
// RUN: mkdir %t
|
||||||
|
// RUN: split-file %s %t
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -std=c++20 %t/m1.cppm -emit-module-interface -o %t/m1.pcm
|
||||||
|
// RUN: %clang_cc1 -std=c++20 %t/m2.cppm -emit-module-interface -o %t/m2.pcm
|
||||||
|
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cc -fsyntax-only \
|
||||||
|
// RUN: -verify
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -std=c++20 %t/m1.cppm -Wall -emit-module-interface -o %t/m1.pcm
|
||||||
|
// RUN: %clang_cc1 -std=c++20 %t/m2.cppm -Wall -emit-module-interface -o %t/m2.pcm
|
||||||
|
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cc -fsyntax-only \
|
||||||
|
// RUN: -verify -Wall
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -std=c++20 %t/m1.cppm -Wdecls-in-multiple-modules -emit-module-interface -o %t/m1.pcm
|
||||||
|
// RUN: %clang_cc1 -std=c++20 %t/m2.cppm -Wdecls-in-multiple-modules -emit-module-interface -o %t/m2.pcm
|
||||||
|
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cc -fsyntax-only \
|
||||||
|
// RUN: -verify -Wdecls-in-multiple-modules -DWARNING
|
||||||
|
|
||||||
|
//--- foo.h
|
||||||
|
#ifndef FOO_H
|
||||||
|
#define FOO_H
|
||||||
|
|
||||||
|
enum E { E1, E2 };
|
||||||
|
|
||||||
|
int a = 43;
|
||||||
|
|
||||||
|
class foo {
|
||||||
|
public:
|
||||||
|
void consume(E, int);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void func() {}
|
||||||
|
|
||||||
|
void fwd_decl();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--- m1.cppm
|
||||||
|
module;
|
||||||
|
#include "foo.h"
|
||||||
|
export module m1;
|
||||||
|
export {
|
||||||
|
using ::foo;
|
||||||
|
using ::a;
|
||||||
|
using ::func;
|
||||||
|
using ::fwd_decl;
|
||||||
|
using ::E;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--- m2.cppm
|
||||||
|
module;
|
||||||
|
#include "foo.h"
|
||||||
|
export module m2;
|
||||||
|
export {
|
||||||
|
using ::foo;
|
||||||
|
using ::a;
|
||||||
|
using ::func;
|
||||||
|
using ::fwd_decl;
|
||||||
|
using ::E;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--- use.cc
|
||||||
|
import m1;
|
||||||
|
import m2;
|
||||||
|
void use();
|
||||||
|
void use() {
|
||||||
|
E e = E1;
|
||||||
|
foo f;
|
||||||
|
f.consume(e, a);
|
||||||
|
func();
|
||||||
|
fwd_decl();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef WARNING
|
||||||
|
// expected-no-diagnostics
|
||||||
|
#else
|
||||||
|
// expected-warning@* {{declaration 'E' is detected to be defined in multiple module units}}
|
||||||
|
// expected-warning@* {{declaration 'foo' is detected to be defined in multiple module units}}
|
||||||
|
// expected-warning@* {{declaration 'a' is detected to be defined in multiple module units}}
|
||||||
|
// expected-warning@* {{declaration 'func' is detected to be defined in multiple module units}}
|
||||||
|
// expected-note@* 1+ {{}}
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user