Skip to content

Commit b7f3a20

Browse files
committed
[cxx-interop] Mangle numeric template arguments
This fixes linker errors when there are multiple instantiations of a templated struct with numeric template parameters. When mangling the name of a C++ template specialization, we currently ignore non-type templated parameters. This causes two different instantiations of the same templated type to have the same mangled name, triggering linker errors. rdar://107757051 (cherry picked from commit d4e8551)
1 parent 6f1cf76 commit b7f3a20

6 files changed

+78
-27
lines changed

lib/ClangImporter/ImportName.cpp

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2195,6 +2195,37 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
21952195
return importNameImpl(classTemplateSpecDecl->getSpecializedTemplate(),
21962196
version, givenName);
21972197
if (!isa<clang::ClassTemplatePartialSpecializationDecl>(D)) {
2198+
auto getSwiftBuiltinTypeName =
2199+
[&](const clang::BuiltinType *builtin) -> std::optional<StringRef> {
2200+
Type swiftType = nullptr;
2201+
switch (builtin->getKind()) {
2202+
case clang::BuiltinType::Void:
2203+
swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(),
2204+
"Void");
2205+
break;
2206+
#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
2207+
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
2208+
swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \
2209+
#SWIFT_TYPE_NAME); \
2210+
break;
2211+
#define MAP_BUILTIN_CCHAR_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
2212+
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
2213+
swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \
2214+
#SWIFT_TYPE_NAME); \
2215+
break;
2216+
#include "swift/ClangImporter/BuiltinMappedTypes.def"
2217+
default:
2218+
break;
2219+
}
2220+
2221+
if (swiftType) {
2222+
if (auto nominal = swiftType->getAs<NominalType>()) {
2223+
return nominal->getDecl()->getNameStr();
2224+
}
2225+
}
2226+
return std::nullopt;
2227+
};
2228+
21982229
// When constructing the name of a C++ template, don't expand all the
21992230
// template, only expand one layer. Here we want to prioritize
22002231
// readability over total completeness.
@@ -2204,38 +2235,16 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
22042235
buffer << "<";
22052236
llvm::interleaveComma(classTemplateSpecDecl->getTemplateArgs().asArray(),
22062237
buffer,
2207-
[&buffer, this, version](const clang::TemplateArgument& arg) {
2238+
[&buffer, this, version, &getSwiftBuiltinTypeName](const clang::TemplateArgument& arg) {
22082239
// Use import name here so builtin types such as "int" map to their
22092240
// Swift equivalent ("Int32").
22102241
if (arg.getKind() == clang::TemplateArgument::Type) {
22112242
auto ty = arg.getAsType().getTypePtr();
22122243
if (auto builtin = dyn_cast<clang::BuiltinType>(ty)) {
22132244
auto &ctx = swiftCtx;
2214-
Type swiftType = nullptr;
2215-
switch (builtin->getKind()) {
2216-
case clang::BuiltinType::Void:
2217-
swiftType = ctx.getNamedSwiftType(ctx.getStdlibModule(), "Void");
2218-
break;
2219-
#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
2220-
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
2221-
swiftType = ctx.getNamedSwiftType(ctx.getStdlibModule(), \
2222-
#SWIFT_TYPE_NAME); \
2223-
break;
2224-
#define MAP_BUILTIN_CCHAR_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
2225-
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
2226-
swiftType = ctx.getNamedSwiftType(ctx.getStdlibModule(), \
2227-
#SWIFT_TYPE_NAME); \
2228-
break;
2229-
#include "swift/ClangImporter/BuiltinMappedTypes.def"
2230-
default:
2231-
break;
2232-
}
2233-
2234-
if (swiftType) {
2235-
if (auto nominal = dyn_cast<NominalType>(swiftType->getCanonicalType())) {
2236-
buffer << nominal->getDecl()->getNameStr();
2237-
return;
2238-
}
2245+
if (auto swiftTypeName = getSwiftBuiltinTypeName(builtin)) {
2246+
buffer << *swiftTypeName;
2247+
return;
22392248
}
22402249
} else {
22412250
// FIXME: Generalize this to cover pointer to
@@ -2276,6 +2285,16 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
22762285
return;
22772286
}
22782287
}
2288+
} else if (arg.getKind() == clang::TemplateArgument::Integral) {
2289+
buffer << "_";
2290+
if (arg.getIntegralType()->isBuiltinType()) {
2291+
if (auto swiftTypeName = getSwiftBuiltinTypeName(
2292+
arg.getIntegralType()->getAs<clang::BuiltinType>())) {
2293+
buffer << *swiftTypeName << "_";
2294+
}
2295+
}
2296+
arg.getAsIntegral().print(buffer, true);
2297+
return;
22792298
}
22802299
buffer << "_";
22812300
});

test/Interop/Cxx/templates/Inputs/class-template-non-type-parameter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ using size_t = __SIZE_TYPE__;
88
template <class T, size_t Size> struct MagicArray { T t[Size]; };
99

1010
typedef MagicArray<int, 2> MagicIntPair;
11+
typedef MagicArray<int, 3> MagicIntTriple;
1112

1213
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_CLASS_TEMPLATE_NON_TYPE_PARAMETER_H
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-experimental-cxx-interop | %FileCheck %s
2+
3+
import ClassTemplateNonTypeParameter
4+
5+
let p = MagicIntPair()
6+
let t = MagicIntTriple()
7+
8+
// CHECK: @"${{s4main1pSo0034MagicArrayInt32_UInt_2_zoAFhhiEngcVvp|s4main1pSo0036MagicArrayInt32_UInt64_2_JsAEiFiuomcVvp}}"
9+
// CHECK: @"${{s4main1tSo0034MagicArrayInt32_UInt_3_zoAFhhiEngcVvp|s4main1tSo0036MagicArrayInt32_UInt64_3_JsAEiFiuomcVvp}}"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=ClassTemplateNonTypeParameter -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
2+
3+
// CHECK: struct MagicArray<T, Size> {
4+
// CHECK: }
5+
6+
// CHECK: struct MagicArray<Int32, _UInt{{.*}}_2> {
7+
// CHECK: init()
8+
// CHECK: init(t: (Int32, Int32))
9+
// CHECK: var t: (Int32, Int32)
10+
// CHECK: }
11+
12+
// CHECK: struct MagicArray<Int32, _UInt{{.*}}_3> {
13+
// CHECK: init()
14+
// CHECK: init(t: (Int32, Int32, Int32))
15+
// CHECK: var t: (Int32, Int32, Int32)
16+
// CHECK: }
17+
18+
// CHECK: typealias MagicIntPair = MagicArray<Int32, _UInt{{.*}}_2>
19+
// CHECK: typealias MagicIntTriple = MagicArray<Int32, _UInt{{.*}}_3>

test/Interop/Cxx/templates/class-template-non-type-parameter.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ var TemplatesTestSuite = TestSuite("TemplatesTestSuite")
1010
TemplatesTestSuite.test("typedeffed-non-type-parameter") {
1111
let pair = MagicIntPair(t: (1, 2))
1212
expectEqual(pair.t, (1, 2))
13+
14+
let triple = MagicIntTriple(t: (1, 2, 3))
15+
expectEqual(triple.t, (1, 2, 3))
1316
}
1417

1518
// TODO: This test doesn't work because Swift doesn't support defaulted generic

test/Interop/Cxx/templates/large-class-templates-module-interface.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212

1313
// TODO: we should not be importing functions that use this type in their
1414
// signature (such as the function below).
15-
// CHECK: mutating func test1() -> RegressionTest.ValExpr<SliceExpr<SliceExpr<Array<Int32>, _>, _>>
15+
// CHECK: mutating func test1() -> RegressionTest.ValExpr<SliceExpr<SliceExpr<Array<Int32>, _Int32_1>, _Int32_1>>

0 commit comments

Comments
 (0)