Skip to content

Commit 18f3bcb

Browse files
committed
[C++20] [Modules] Correct the linkage for template instantiations in
named modules Close #97313 In the previous patch (#75912), I made an oversight that I ignored the templates in named module when calculating the linkage for the vtables. In this patch, I tried to correct the behavior by merging the logics to calculate the linkage with key functions with named modules.
1 parent c72cb27 commit 18f3bcb

File tree

2 files changed

+142
-19
lines changed

2 files changed

+142
-19
lines changed

clang/lib/CodeGen/CGVTables.cpp

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,33 +1079,38 @@ CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
10791079
if (!RD->isExternallyVisible())
10801080
return llvm::GlobalVariable::InternalLinkage;
10811081

1082-
// V-tables for non-template classes with an owning module are always
1083-
// uniquely emitted in that module.
1084-
if (RD->isInNamedModule())
1085-
return llvm::GlobalVariable::ExternalLinkage;
1086-
1087-
// We're at the end of the translation unit, so the current key
1088-
// function is fully correct.
1089-
const CXXMethodDecl *keyFunction = Context.getCurrentKeyFunction(RD);
1090-
if (keyFunction && !RD->hasAttr<DLLImportAttr>()) {
1082+
bool IsInNamedModule = RD->isInNamedModule();
1083+
// If the CXXRecordDecl are not in a module unit, we need to get
1084+
// its key function. We're at the end of the translation unit, so the current
1085+
// key function is fully correct.
1086+
const CXXMethodDecl *keyFunction =
1087+
IsInNamedModule ? nullptr : Context.getCurrentKeyFunction(RD);
1088+
if (IsInNamedModule || (keyFunction && !RD->hasAttr<DLLImportAttr>())) {
10911089
// If this class has a key function, use that to determine the
10921090
// linkage of the vtable.
10931091
const FunctionDecl *def = nullptr;
1094-
if (keyFunction->hasBody(def))
1092+
if (keyFunction && keyFunction->hasBody(def))
10951093
keyFunction = cast<CXXMethodDecl>(def);
10961094

1097-
switch (keyFunction->getTemplateSpecializationKind()) {
1098-
case TSK_Undeclared:
1099-
case TSK_ExplicitSpecialization:
1095+
bool IsExternalDefinition =
1096+
IsInNamedModule ? RD->shouldEmitInExternalSource() : !def;
1097+
1098+
TemplateSpecializationKind Kind =
1099+
IsInNamedModule ? RD->getTemplateSpecializationKind()
1100+
: keyFunction->getTemplateSpecializationKind();
1101+
1102+
switch (Kind) {
1103+
case TSK_Undeclared:
1104+
case TSK_ExplicitSpecialization:
11001105
assert(
1101-
(def || CodeGenOpts.OptimizationLevel > 0 ||
1106+
(IsInNamedModule || def || CodeGenOpts.OptimizationLevel > 0 ||
11021107
CodeGenOpts.getDebugInfo() != llvm::codegenoptions::NoDebugInfo) &&
1103-
"Shouldn't query vtable linkage without key function, "
1104-
"optimizations, or debug info");
1105-
if (!def && CodeGenOpts.OptimizationLevel > 0)
1108+
"Shouldn't query vtable linkage without the class in module units, "
1109+
"key function, optimizations, or debug info");
1110+
if (IsExternalDefinition && CodeGenOpts.OptimizationLevel > 0)
11061111
return llvm::GlobalVariable::AvailableExternallyLinkage;
11071112

1108-
if (keyFunction->isInlined())
1113+
if (keyFunction && keyFunction->isInlined())
11091114
return !Context.getLangOpts().AppleKext
11101115
? llvm::GlobalVariable::LinkOnceODRLinkage
11111116
: llvm::Function::InternalLinkage;
@@ -1124,7 +1129,7 @@ CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
11241129

11251130
case TSK_ExplicitInstantiationDeclaration:
11261131
llvm_unreachable("Should not have been asked to emit this");
1127-
}
1132+
}
11281133
}
11291134

11301135
// -fapple-kext mode does not support weak linkage, so we must use

clang/test/Modules/pr97313.cppm

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// REQUIRES: !system-windows
2+
//
3+
// RUN: rm -rf %t
4+
// RUN: mkdir -p %t
5+
// RUN: split-file %s %t
6+
//
7+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/Base.cppm \
8+
// RUN: -emit-module-interface -o %t/Base.pcm
9+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/Sub.cppm \
10+
// RUN: -emit-module-interface -o %t/Sub.pcm -fprebuilt-module-path=%t
11+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/Sub.pcm \
12+
// RUN: -emit-llvm -o %t/Sub.pcm -o - -fprebuilt-module-path=%t | \
13+
// RUN: FileCheck %t/Sub.cppm
14+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/main.cpp \
15+
// RUN: -emit-llvm -fprebuilt-module-path=%t -o - | FileCheck %t/main.cpp
16+
//
17+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/Mod.cppm \
18+
// RUN: -emit-module-interface -o %t/Mod.pcm
19+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/Mod.pcm \
20+
// RUN: -emit-llvm -o - | FileCheck %t/Mod.cppm
21+
// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/Use.cpp \
22+
// RUN: -emit-llvm -fprebuilt-module-path=%t -o - | \
23+
// RUN: FileCheck %t/Use.cpp
24+
25+
//--- Base.cppm
26+
export module Base;
27+
28+
export template <class>
29+
class Base
30+
{
31+
public:
32+
constexpr Base();
33+
constexpr virtual ~Base();
34+
};
35+
36+
template <class X>
37+
constexpr Base<X>::Base() = default;
38+
39+
template <class X>
40+
constexpr Base<X>::~Base() = default;
41+
42+
//--- Sub.cppm
43+
export module Sub;
44+
export import Base;
45+
46+
export class Sub : public Base<int>
47+
{
48+
};
49+
50+
// CHECK: @_ZTIW4Base4BaseIiE = {{.*}}linkonce_odr
51+
52+
//--- main.cpp
53+
import Sub;
54+
55+
int main()
56+
{
57+
Base<int> *b = new Sub();
58+
delete b;
59+
}
60+
61+
// CHECK: @_ZTIW4Base4BaseIiE = {{.*}}linkonce_odr
62+
63+
//--- Mod.cppm
64+
export module Mod;
65+
66+
export class NonTemplate {
67+
public:
68+
virtual ~NonTemplate();
69+
};
70+
71+
// CHECK: @_ZTIW3Mod11NonTemplate = {{.*}}constant
72+
73+
export template <class C>
74+
class Template {
75+
public:
76+
virtual ~Template();
77+
};
78+
79+
export template<>
80+
class Template<char> {
81+
public:
82+
virtual ~Template();
83+
};
84+
85+
// CHECK: @_ZTIW3Mod8TemplateIcE = {{.*}}constant
86+
87+
export template class Template<unsigned>;
88+
89+
// CHECK: @_ZTIW3Mod8TemplateIjE = {{.*}}weak_odr
90+
91+
export extern template class Template<double>;
92+
93+
auto v = new Template<signed int>();
94+
95+
// CHECK: @_ZTIW3Mod8TemplateIiE = {{.*}}linkonce_odr
96+
97+
//--- Use.cpp
98+
import Mod;
99+
100+
auto v1 = new NonTemplate();
101+
auto v2 = new Template<char>();
102+
auto v3 = new Template<unsigned>();
103+
auto v4 = new Template<double>();
104+
auto v5 = new Template<signed int>();
105+
auto v6 = new Template<NonTemplate>();
106+
107+
// CHECK: @_ZTVW3Mod11NonTemplate = {{.*}}external
108+
// CHECK: @_ZTVW3Mod8TemplateIcE = {{.*}}external
109+
// CHECK: @_ZTVW3Mod8TemplateIjE = {{.*}}weak_odr
110+
// CHECK: @_ZTSW3Mod8TemplateIjE = {{.*}}weak_odr
111+
// CHECK: @_ZTIW3Mod8TemplateIjE = {{.*}}weak_odr
112+
// CHECK: @_ZTVW3Mod8TemplateIdE = {{.*}}external
113+
// CHECK: @_ZTVW3Mod8TemplateIiE = {{.*}}linkonce_odr
114+
// CHECK: @_ZTSW3Mod8TemplateIiE = {{.*}}linkonce_odr
115+
// CHECK: @_ZTIW3Mod8TemplateIiE = {{.*}}linkonce_odr
116+
// CHECK: @_ZTVW3Mod8TemplateIS_11NonTemplateE = {{.*}}linkonce_odr
117+
// CHECK: @_ZTSW3Mod8TemplateIS_11NonTemplateE = {{.*}}linkonce_odr
118+
// CHECK: @_ZTIW3Mod8TemplateIS_11NonTemplateE = {{.*}}linkonce_odr

0 commit comments

Comments
 (0)