Skip to content

Commit d4e8551

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
1 parent 80e4e04 commit d4e8551

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
@@ -2201,6 +2201,37 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
22012201
return importNameImpl(classTemplateSpecDecl->getSpecializedTemplate(),
22022202
version, givenName);
22032203
if (!isa<clang::ClassTemplatePartialSpecializationDecl>(D)) {
2204+
auto getSwiftBuiltinTypeName =
2205+
[&](const clang::BuiltinType *builtin) -> std::optional<StringRef> {
2206+
Type swiftType = nullptr;
2207+
switch (builtin->getKind()) {
2208+
case clang::BuiltinType::Void:
2209+
swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(),
2210+
"Void");
2211+
break;
2212+
#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
2213+
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
2214+
swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \
2215+
#SWIFT_TYPE_NAME); \
2216+
break;
2217+
#define MAP_BUILTIN_CCHAR_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
2218+
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
2219+
swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \
2220+
#SWIFT_TYPE_NAME); \
2221+
break;
2222+
#include "swift/ClangImporter/BuiltinMappedTypes.def"
2223+
default:
2224+
break;
2225+
}
2226+
2227+
if (swiftType) {
2228+
if (auto nominal = swiftType->getAs<NominalType>()) {
2229+
return nominal->getDecl()->getNameStr();
2230+
}
2231+
}
2232+
return std::nullopt;
2233+
};
2234+
22042235
// When constructing the name of a C++ template, don't expand all the
22052236
// template, only expand one layer. Here we want to prioritize
22062237
// readability over total completeness.
@@ -2210,38 +2241,16 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
22102241
buffer << "<";
22112242
llvm::interleaveComma(classTemplateSpecDecl->getTemplateArgs().asArray(),
22122243
buffer,
2213-
[&buffer, this, version](const clang::TemplateArgument& arg) {
2244+
[&buffer, this, version, &getSwiftBuiltinTypeName](const clang::TemplateArgument& arg) {
22142245
// Use import name here so builtin types such as "int" map to their
22152246
// Swift equivalent ("Int32").
22162247
if (arg.getKind() == clang::TemplateArgument::Type) {
22172248
auto ty = arg.getAsType().getTypePtr();
22182249
if (auto builtin = dyn_cast<clang::BuiltinType>(ty)) {
22192250
auto &ctx = swiftCtx;
2220-
Type swiftType = nullptr;
2221-
switch (builtin->getKind()) {
2222-
case clang::BuiltinType::Void:
2223-
swiftType = ctx.getNamedSwiftType(ctx.getStdlibModule(), "Void");
2224-
break;
2225-
#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
2226-
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
2227-
swiftType = ctx.getNamedSwiftType(ctx.getStdlibModule(), \
2228-
#SWIFT_TYPE_NAME); \
2229-
break;
2230-
#define MAP_BUILTIN_CCHAR_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
2231-
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
2232-
swiftType = ctx.getNamedSwiftType(ctx.getStdlibModule(), \
2233-
#SWIFT_TYPE_NAME); \
2234-
break;
2235-
#include "swift/ClangImporter/BuiltinMappedTypes.def"
2236-
default:
2237-
break;
2238-
}
2239-
2240-
if (swiftType) {
2241-
if (auto nominal = dyn_cast<NominalType>(swiftType->getCanonicalType())) {
2242-
buffer << nominal->getDecl()->getNameStr();
2243-
return;
2244-
}
2251+
if (auto swiftTypeName = getSwiftBuiltinTypeName(builtin)) {
2252+
buffer << *swiftTypeName;
2253+
return;
22452254
}
22462255
} else {
22472256
// FIXME: Generalize this to cover pointer to
@@ -2282,6 +2291,16 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
22822291
return;
22832292
}
22842293
}
2294+
} else if (arg.getKind() == clang::TemplateArgument::Integral) {
2295+
buffer << "_";
2296+
if (arg.getIntegralType()->isBuiltinType()) {
2297+
if (auto swiftTypeName = getSwiftBuiltinTypeName(
2298+
arg.getIntegralType()->getAs<clang::BuiltinType>())) {
2299+
buffer << *swiftTypeName << "_";
2300+
}
2301+
}
2302+
arg.getAsIntegral().print(buffer, true);
2303+
return;
22852304
}
22862305
buffer << "_";
22872306
});

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)