Skip to content

[C++20] [Modules] Warn for duplicated decls in mutliple module units #105799

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSerializationKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ def warn_module_system_bit_conflict : Warning<
"as a non-system module; any difference in diagnostic options will be ignored">,
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<
"failed to find module file for module '%0'">;
} // let CategoryName
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,12 @@ class ASTReader
/// performed deduplication.
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.
bool ReadLexicalDeclContextStorage(ModuleFile &M,
llvm::BitstreamCursor &Cursor,
Expand Down
39 changes: 39 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9955,6 +9955,45 @@ void ASTReader::finishPendingActions() {
}
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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get a clang warning here:

../../clang/lib/Serialization/ASTReader.cpp:9978:27: error: lambda capture 'this' is not used [-Werror,-Wunused-lambda-capture]
    auto hasDefinition = [this, &hasDefinitionImpl](Decl *D) {
                          ^~~~~
1 error generated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks and this looks to be fixed by someoneelse. Thanks again!

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
// this now (delayed) so that we can be sure that the declaration chains
// have been fully wired up (hasBody relies on this).
Expand Down
21 changes: 18 additions & 3 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
static void attachPreviousDecl(ASTReader &Reader, Decl *D, Decl *Previous,
Decl *Canon);

static void checkMultipleDefinitionInNamedModules(ASTReader &Reader, Decl *D,
Decl *Previous);

template <typename DeclT>
static void attachLatestDeclImpl(Redeclarable<DeclT> *D, Decl *Latest);
static void attachLatestDeclImpl(...);
Expand Down Expand Up @@ -3690,8 +3693,9 @@ static void inheritDefaultTemplateArguments(ASTContext &Context,
// [basic.link]/p10:
// If two declarations of an entity are attached to different modules,
// the program is ill-formed;
static void checkMultipleDefinitionInNamedModules(ASTReader &Reader, Decl *D,
Decl *Previous) {
void ASTDeclReader::checkMultipleDefinitionInNamedModules(ASTReader &Reader,
Decl *D,
Decl *Previous) {
// If it is previous implcitly introduced, it is not meaningful to
// diagnose it.
if (Previous->isImplicit())
Expand Down Expand Up @@ -3721,8 +3725,19 @@ static void checkMultipleDefinitionInNamedModules(ASTReader &Reader, Decl *D,
return;

// 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;
}

// It is fine if they are in the same module.
if (Reader.getContext().isInSameModule(M, D->getOwningModule()))
Expand Down
83 changes: 83 additions & 0 deletions clang/test/Modules/warn-duplicated-decls-in-module-units.cppm
Original file line number Diff line number Diff line change
@@ -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