Skip to content

Commit 2db239a

Browse files
committed
[C++20] [Modules] Improve the import-and-include pattern
The import and include problem is a long-standing issue with the use of C++20 modules. This patch tried to improve this by skipping parsing class and functions if their declaration is already defined in modules. The scale of the patch itself is small as the patch reuses previous optimization. Maybe we can skip parsing other declarations in the future. But the patch itself should be good.
1 parent 78e3ab3 commit 2db239a

File tree

6 files changed

+117
-26
lines changed

6 files changed

+117
-26
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15328,6 +15328,16 @@ class Sema final : public SemaBase {
1532815328
NamedDecl *Hidden;
1532915329
return hasVisibleDefinition(const_cast<NamedDecl *>(D), &Hidden);
1533015330
}
15331+
/// Determine if \p D has a definition which allows we redefine it in current
15332+
/// TU. \p Suggested is the definition that should be made visible to expose
15333+
/// the definition.
15334+
bool isRedefinitionAllowedFor(NamedDecl *D, NamedDecl **Suggested,
15335+
bool &Visible);
15336+
bool isRedefinitionAllowedFor(const NamedDecl *D, bool &Visible) {
15337+
NamedDecl *Hidden;
15338+
return isRedefinitionAllowedFor(const_cast<NamedDecl *>(D), &Hidden,
15339+
Visible);
15340+
}
1533115341

1533215342
/// Determine if \p D has a reachable definition. If not, suggest a
1533315343
/// declaration that should be made reachable to expose the definition.

clang/lib/Sema/SemaDecl.cpp

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15837,17 +15837,18 @@ Sema::CheckForFunctionRedefinition(FunctionDecl *FD,
1583715837
if (TypoCorrectedFunctionDefinitions.count(Definition))
1583815838
return;
1583915839

15840-
// If we don't have a visible definition of the function, and it's inline or
15841-
// a template, skip the new definition.
15842-
if (SkipBody && !hasVisibleDefinition(Definition) &&
15840+
bool DefinitionVisible = false;
15841+
if (SkipBody && isRedefinitionAllowedFor(Definition, DefinitionVisible) &&
1584315842
(Definition->getFormalLinkage() == Linkage::Internal ||
1584415843
Definition->isInlined() || Definition->getDescribedFunctionTemplate() ||
1584515844
Definition->getNumTemplateParameterLists())) {
1584615845
SkipBody->ShouldSkip = true;
1584715846
SkipBody->Previous = const_cast<FunctionDecl*>(Definition);
15848-
if (auto *TD = Definition->getDescribedFunctionTemplate())
15849-
makeMergedDefinitionVisible(TD);
15850-
makeMergedDefinitionVisible(const_cast<FunctionDecl*>(Definition));
15847+
if (!DefinitionVisible) {
15848+
if (auto *TD = Definition->getDescribedFunctionTemplate())
15849+
makeMergedDefinitionVisible(TD);
15850+
makeMergedDefinitionVisible(const_cast<FunctionDecl *>(Definition));
15851+
}
1585115852
return;
1585215853
}
1585315854

@@ -18196,8 +18197,10 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
1819618197
// However, ensure the decl passes the structural compatibility
1819718198
// check in C11 6.2.7/1 (or 6.1.2.6/1 in C89).
1819818199
NamedDecl *Hidden = nullptr;
18199-
if (SkipBody && (!hasVisibleDefinition(Def, &Hidden) ||
18200-
getLangOpts().C23)) {
18200+
bool HiddenDefVisible = false;
18201+
if (SkipBody &&
18202+
(isRedefinitionAllowedFor(Def, &Hidden, HiddenDefVisible) ||
18203+
getLangOpts().C23)) {
1820118204
// There is a definition of this tag, but it is not visible.
1820218205
// We explicitly make use of C++'s one definition rule here,
1820318206
// and assume that this definition is identical to the hidden
@@ -18213,13 +18216,15 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
1821318216

1821418217
ProcessDeclAttributeList(S, SkipBody->New, Attrs);
1821518218
return Def;
18216-
} else {
18217-
SkipBody->ShouldSkip = true;
18218-
SkipBody->Previous = Def;
18219-
makeMergedDefinitionVisible(Hidden);
18220-
// Carry on and handle it like a normal definition. We'll
18221-
// skip starting the definition later.
1822218219
}
18220+
18221+
SkipBody->ShouldSkip = true;
18222+
SkipBody->Previous = Def;
18223+
if (!HiddenDefVisible && Hidden)
18224+
makeMergedDefinitionVisible(Hidden);
18225+
// Carry on and handle it like a normal definition. We'll
18226+
// skip starting the definition later.
18227+
1822318228
} else if (!IsExplicitSpecializationAfterInstantiation) {
1822418229
// A redeclaration in function prototype scope in C isn't
1822518230
// visible elsewhere, so merely issue a warning.
@@ -20842,3 +20847,13 @@ bool Sema::shouldIgnoreInHostDeviceCheck(FunctionDecl *Callee) {
2084220847
return LangOpts.CUDA && !LangOpts.CUDAIsDevice &&
2084320848
CUDA().IdentifyTarget(Callee) == CUDAFunctionTarget::Global;
2084420849
}
20850+
20851+
bool Sema::isRedefinitionAllowedFor(NamedDecl *D, NamedDecl **Suggested,
20852+
bool &Visible) {
20853+
Visible = hasVisibleDefinition(D, Suggested);
20854+
// The redefinition of D in the **current** TU is allowed if D is invisible or
20855+
// D is defined in the global module of other module units. We didn't check if
20856+
// it is in global module as, we'll check the redefinition in named module
20857+
// later with better diagnostic message.
20858+
return D->isInAnotherModuleUnit() || !Visible;
20859+
}

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,14 +2078,19 @@ DeclResult Sema::CheckClassTemplate(
20782078
// If we have a prior definition that is not visible, treat this as
20792079
// simply making that previous definition visible.
20802080
NamedDecl *Hidden = nullptr;
2081-
if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) {
2081+
bool HiddenDefVisible = false;
2082+
if (SkipBody &&
2083+
isRedefinitionAllowedFor(Def, &Hidden, HiddenDefVisible)) {
20822084
SkipBody->ShouldSkip = true;
20832085
SkipBody->Previous = Def;
2084-
auto *Tmpl = cast<CXXRecordDecl>(Hidden)->getDescribedClassTemplate();
2085-
assert(Tmpl && "original definition of a class template is not a "
2086-
"class template?");
2087-
makeMergedDefinitionVisible(Hidden);
2088-
makeMergedDefinitionVisible(Tmpl);
2086+
if (!HiddenDefVisible && Hidden) {
2087+
auto *Tmpl =
2088+
cast<CXXRecordDecl>(Hidden)->getDescribedClassTemplate();
2089+
assert(Tmpl && "original definition of a class template is not a "
2090+
"class template?");
2091+
makeMergedDefinitionVisible(Hidden);
2092+
makeMergedDefinitionVisible(Tmpl);
2093+
}
20892094
} else {
20902095
Diag(NameLoc, diag::err_redefinition) << Name;
20912096
Diag(Def->getLocation(), diag::note_previous_definition);
@@ -8956,10 +8961,13 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
89568961
if (TUK == TagUseKind::Definition) {
89578962
RecordDecl *Def = Specialization->getDefinition();
89588963
NamedDecl *Hidden = nullptr;
8959-
if (Def && SkipBody && !hasVisibleDefinition(Def, &Hidden)) {
8964+
bool HiddenDefVisible = false;
8965+
if (Def && SkipBody &&
8966+
isRedefinitionAllowedFor(Def, &Hidden, HiddenDefVisible)) {
89608967
SkipBody->ShouldSkip = true;
89618968
SkipBody->Previous = Def;
8962-
makeMergedDefinitionVisible(Hidden);
8969+
if (!HiddenDefVisible && Hidden)
8970+
makeMergedDefinitionVisible(Hidden);
89638971
} else if (Def) {
89648972
SourceRange Range(TemplateNameLoc, RAngleLoc);
89658973
Diag(TemplateNameLoc, diag::err_redefinition) << Specialization << Range;

clang/test/CXX/drs/cwg279.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ extern S2 *q2;
4646

4747
// FIXME: This is well-formed, because [basic.def.odr]/15 is satisfied.
4848
struct S3 {};
49-
// since-cxx20-error@-1 {{redefinition of 'S3'}}
50-
// since-cxx20-note@cwg279_A.cppm:23 {{previous definition is here}}
49+
// since-cxx20-error@-1 {{declaration of 'S3' in the global module follows declaration in module cwg279_A}}
50+
// since-cxx20-note@cwg279_A.cppm:23 {{previous declaration is here}}
5151
extern S3 *q3;
5252
// since-cxx20-error@-1 {{declaration of 'q3' in the global module follows declaration in module cwg279_A}}
5353
// since-cxx20-note@cwg279_A.cppm:24 {{previous declaration is here}}

clang/test/Modules/redundant-template-default-arg2.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ int v2; // expected-error {{declaration of 'v2' in the global module follows dec
3333
// [email protected]:6 {{previous declaration is here}}
3434

3535
template <typename T>
36-
class my_array {}; // expected-error {{redefinition of 'my_array'}}
37-
// [email protected]:9 {{previous definition is here}}
36+
class my_array {}; // expected-error {{declaration of 'my_array' in the global module follows declaration in module foo}}
37+
// [email protected]:9 {{previous declaration is here}}
3838

3939
template <template <typename> typename C = my_array>
4040
int v3; // expected-error {{declaration of 'v3' in the global module follows declaration in module foo}}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/a.pcm
6+
// RUN: %clang_cc1 -std=c++20 %t/a.cpp -fmodule-file=a=%t/a.pcm -ast-dump | FileCheck %s
7+
8+
// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-module-interface -o %t/a.pcm
9+
// RUN: %clang_cc1 -std=c++20 %t/a.cpp -fmodule-file=a=%t/a.pcm -ast-dump | FileCheck %s
10+
11+
//--- a.h
12+
namespace a {
13+
class A {
14+
public:
15+
int aaaa;
16+
17+
int get() {
18+
return aaaa;
19+
}
20+
};
21+
22+
23+
template <class T>
24+
class B {
25+
public:
26+
B(T t): t(t) {}
27+
T t;
28+
};
29+
30+
using BI = B<int>;
31+
32+
inline int get(A a, BI b) {
33+
return a.get() + b.t;
34+
}
35+
36+
}
37+
38+
//--- a.cppm
39+
export module a;
40+
41+
export extern "C++" {
42+
#include "a.h"
43+
}
44+
45+
//--- a.cpp
46+
import a;
47+
#include "a.h"
48+
49+
int test() {
50+
a::A aa;
51+
a::BI bb(43);
52+
return get(aa, bb);
53+
}
54+
55+
// CHECK-NOT: DefinitionData
56+
// CHECK: FunctionDecl {{.*}} get 'int (A, BI)' {{.*}}
57+
// CHECK-NOT: CompoundStmt
58+
// CHECK: FunctionDecl {{.*}} test {{.*}}

0 commit comments

Comments
 (0)