Skip to content

Commit b7c9cfe

Browse files
committed
[cxx-interop] Use unique mangling for distinct C++ class template specializations
This makes sure we are printing more than one level of C++ template specializations when emitting a Swift struct name. For instance, `std::__wrap_iter<char*>` and `std::__wrap_iter<const char*>` are currently imported with the same name in Swift. This means the mangled string will be the same for these specializations, despite them being distinct types. This causes mangling errors. rdar://117485399
1 parent 7b5db49 commit b7c9cfe

File tree

6 files changed

+205
-105
lines changed

6 files changed

+205
-105
lines changed

lib/ClangImporter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_gyb_target(generated_sorted_cf_database
88
add_swift_host_library(swiftClangImporter STATIC
99
CFTypeInfo.cpp
1010
ClangAdapter.cpp
11+
ClangClassTemplateNamePrinter.cpp
1112
ClangDerivedConformances.cpp
1213
ClangDiagnosticConsumer.cpp
1314
ClangImporter.cpp
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
//===--- ClangClassTemplateNamePrinter.cpp --------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "ClangClassTemplateNamePrinter.h"
14+
#include "ImporterImpl.h"
15+
#include "clang/AST/TypeVisitor.h"
16+
17+
using namespace swift;
18+
using namespace swift::importer;
19+
20+
struct TemplateInstantiationNamePrinter
21+
: clang::TypeVisitor<TemplateInstantiationNamePrinter, std::string> {
22+
ASTContext &swiftCtx;
23+
NameImporter *nameImporter;
24+
ImportNameVersion version;
25+
26+
TemplateInstantiationNamePrinter(ASTContext &swiftCtx,
27+
NameImporter *nameImporter,
28+
ImportNameVersion version)
29+
: swiftCtx(swiftCtx), nameImporter(nameImporter), version(version) {}
30+
31+
std::string VisitBuiltinType(const clang::BuiltinType *type) {
32+
Type swiftType = nullptr;
33+
switch (type->getKind()) {
34+
case clang::BuiltinType::Void:
35+
swiftType =
36+
swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), "Void");
37+
break;
38+
#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
39+
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
40+
swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \
41+
#SWIFT_TYPE_NAME); \
42+
break;
43+
#define MAP_BUILTIN_CCHAR_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
44+
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
45+
swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \
46+
#SWIFT_TYPE_NAME); \
47+
break;
48+
#include "swift/ClangImporter/BuiltinMappedTypes.def"
49+
default:
50+
break;
51+
}
52+
53+
if (swiftType) {
54+
if (swiftType->is<NominalType>()) {
55+
return swiftType->getStringAsComponent();
56+
}
57+
}
58+
return "_";
59+
}
60+
61+
std::string VisitRecordType(const clang::RecordType *type) {
62+
auto tagDecl = type->getAsTagDecl();
63+
if (auto namedArg = dyn_cast_or_null<clang::NamedDecl>(tagDecl)) {
64+
llvm::SmallString<128> storage;
65+
llvm::raw_svector_ostream buffer(storage);
66+
nameImporter->importName(namedArg, version, clang::DeclarationName())
67+
.getDeclName()
68+
.print(buffer);
69+
return buffer.str().str();
70+
}
71+
return "_";
72+
}
73+
74+
std::string VisitPointerType(const clang::PointerType *type) {
75+
std::string pointeeResult = Visit(type->getPointeeType().getTypePtr());
76+
77+
enum class TagTypeDecorator { None, UnsafePointer, UnsafeMutablePointer };
78+
79+
// If this is a pointer to foreign reference type, we should not wrap
80+
// it in Unsafe(Mutable)?Pointer, since it will be imported as a class
81+
// in Swift.
82+
bool isReferenceType = false;
83+
if (auto tagDecl = type->getPointeeType()->getAsTagDecl()) {
84+
if (auto *rd = dyn_cast<clang::RecordDecl>(tagDecl))
85+
isReferenceType =
86+
ClangImporter::Implementation::recordHasReferenceSemantics(
87+
rd, swiftCtx);
88+
}
89+
90+
TagTypeDecorator decorator;
91+
if (!isReferenceType)
92+
decorator = type->getPointeeType().isConstQualified()
93+
? TagTypeDecorator::UnsafePointer
94+
: TagTypeDecorator::UnsafeMutablePointer;
95+
else
96+
decorator = TagTypeDecorator::None;
97+
98+
llvm::SmallString<128> storage;
99+
llvm::raw_svector_ostream buffer(storage);
100+
if (decorator != TagTypeDecorator::None)
101+
buffer << (decorator == TagTypeDecorator::UnsafePointer
102+
? "UnsafePointer"
103+
: "UnsafeMutablePointer")
104+
<< '<';
105+
buffer << pointeeResult;
106+
if (decorator != TagTypeDecorator::None)
107+
buffer << '>';
108+
109+
return buffer.str().str();
110+
}
111+
};
112+
113+
std::string swift::importer::printClassTemplateSpecializationName(
114+
const clang::ClassTemplateSpecializationDecl *decl, ASTContext &swiftCtx,
115+
NameImporter *nameImporter, ImportNameVersion version) {
116+
TemplateInstantiationNamePrinter templateNamePrinter(swiftCtx, nameImporter,
117+
version);
118+
119+
// TODO: the following logic should probably be a ConstTemplateArgumentVisitor
120+
llvm::SmallString<128> storage;
121+
llvm::raw_svector_ostream buffer(storage);
122+
decl->printName(buffer);
123+
buffer << "<";
124+
llvm::interleaveComma(
125+
decl->getTemplateArgs().asArray(), buffer,
126+
[&buffer, &templateNamePrinter](const clang::TemplateArgument &arg) {
127+
// Use import name here so builtin types such as "int" map to their
128+
// Swift equivalent ("CInt").
129+
if (arg.getKind() == clang::TemplateArgument::Type) {
130+
auto ty = arg.getAsType().getTypePtr();
131+
buffer << templateNamePrinter.Visit(ty);
132+
return;
133+
} else if (arg.getKind() == clang::TemplateArgument::Integral) {
134+
buffer << "_";
135+
if (arg.getIntegralType()->isBuiltinType()) {
136+
buffer << templateNamePrinter.Visit(
137+
arg.getIntegralType().getTypePtr())
138+
<< "_";
139+
}
140+
arg.getAsIntegral().print(buffer, true);
141+
return;
142+
}
143+
buffer << "_";
144+
});
145+
buffer << ">";
146+
return buffer.str().str();
147+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===--- ClangClassTemplateNamePrinter.h ------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_CLANG_TEMPLATE_NAME_PRINTER_H
14+
#define SWIFT_CLANG_TEMPLATE_NAME_PRINTER_H
15+
16+
#include "ImportName.h"
17+
#include "swift/AST/ASTContext.h"
18+
#include "clang/AST/DeclTemplate.h"
19+
20+
namespace swift {
21+
namespace importer {
22+
23+
/// Returns a Swift representation of a C++ class template specialization name,
24+
/// e.g. "vector<CWideChar, allocator<CWideChar>>".
25+
///
26+
/// This expands the entire tree of template instantiation names recursively.
27+
/// While printing deep instantiation levels might not increase readability, it
28+
/// is important to do because the C++ templated class names get mangled,
29+
/// therefore they must be unique for different instantiations.
30+
///
31+
/// This function does not instantiate any templates and does not modify the AST
32+
/// in any way.
33+
std::string printClassTemplateSpecializationName(
34+
const clang::ClassTemplateSpecializationDecl *decl, ASTContext &swiftCtx,
35+
NameImporter *nameImporter, ImportNameVersion version);
36+
37+
} // namespace importer
38+
} // namespace swift
39+
40+
#endif // SWIFT_CLANG_TEMPLATE_NAME_PRINTER_H

lib/ClangImporter/ImportName.cpp

Lines changed: 4 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
#include "CFTypeInfo.h"
19+
#include "ClangClassTemplateNamePrinter.h"
1920
#include "ClangDiagnosticConsumer.h"
2021
#include "ImporterImpl.h"
2122
#include "swift/AST/ASTContext.h"
@@ -2214,111 +2215,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
22142215
return importNameImpl(classTemplateSpecDecl->getSpecializedTemplate(),
22152216
version, givenName);
22162217
if (!isa<clang::ClassTemplatePartialSpecializationDecl>(D)) {
2217-
auto getSwiftBuiltinTypeName =
2218-
[&](const clang::BuiltinType *builtin) -> std::optional<std::string> {
2219-
Type swiftType = nullptr;
2220-
switch (builtin->getKind()) {
2221-
case clang::BuiltinType::Void:
2222-
swiftType = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(),
2223-
"Void");
2224-
break;
2225-
#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
2226-
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
2227-
swiftType = swiftCtx.getNamedSwiftType(swiftCtx.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 = swiftCtx.getNamedSwiftType(swiftCtx.getStdlibModule(), \
2233-
#SWIFT_TYPE_NAME); \
2234-
break;
2235-
#include "swift/ClangImporter/BuiltinMappedTypes.def"
2236-
default:
2237-
break;
2238-
}
2239-
2240-
if (swiftType) {
2241-
if (swiftType->is<NominalType>()) {
2242-
return swiftType->getStringAsComponent();
2243-
}
2244-
}
2245-
return std::nullopt;
2246-
};
2247-
2248-
// When constructing the name of a C++ template, don't expand all the
2249-
// template, only expand one layer. Here we want to prioritize
2250-
// readability over total completeness.
2251-
llvm::SmallString<128> storage;
2252-
llvm::raw_svector_ostream buffer(storage);
2253-
D->printName(buffer);
2254-
buffer << "<";
2255-
llvm::interleaveComma(classTemplateSpecDecl->getTemplateArgs().asArray(),
2256-
buffer,
2257-
[&buffer, this, version, &getSwiftBuiltinTypeName](const clang::TemplateArgument& arg) {
2258-
// Use import name here so builtin types such as "int" map to their
2259-
// Swift equivalent ("Int32").
2260-
if (arg.getKind() == clang::TemplateArgument::Type) {
2261-
auto ty = arg.getAsType().getTypePtr();
2262-
if (auto builtin = dyn_cast<clang::BuiltinType>(ty)) {
2263-
if (auto swiftTypeName = getSwiftBuiltinTypeName(builtin)) {
2264-
buffer << *swiftTypeName;
2265-
return;
2266-
}
2267-
} else {
2268-
// FIXME: Generalize this to cover pointer to
2269-
// builtin type too.
2270-
// Check if this a struct/class
2271-
// or a pointer/reference to a struct/class.
2272-
auto *tagDecl = ty->getAsTagDecl();
2273-
enum class TagTypeDecorator {
2274-
None,
2275-
UnsafePointer,
2276-
UnsafeMutablePointer
2277-
};
2278-
TagTypeDecorator decorator = TagTypeDecorator::None;
2279-
if (!tagDecl && ty->isPointerType()) {
2280-
tagDecl = ty->getPointeeType()->getAsTagDecl();
2281-
if (tagDecl) {
2282-
bool isReferenceType = false;
2283-
if (auto *rd = dyn_cast<clang::RecordDecl>(tagDecl))
2284-
isReferenceType = ClangImporter::Implementation::
2285-
recordHasReferenceSemantics(rd, swiftCtx);
2286-
if (!isReferenceType)
2287-
decorator = ty->getPointeeType().isConstQualified()
2288-
? TagTypeDecorator::UnsafePointer
2289-
: TagTypeDecorator::UnsafeMutablePointer;
2290-
}
2291-
}
2292-
if (auto namedArg = dyn_cast_or_null<clang::NamedDecl>(tagDecl)) {
2293-
if (decorator != TagTypeDecorator::None)
2294-
buffer << (decorator == TagTypeDecorator::UnsafePointer
2295-
? "UnsafePointer"
2296-
: "UnsafeMutablePointer")
2297-
<< '<';
2298-
importNameImpl(namedArg, version, clang::DeclarationName())
2299-
.getDeclName()
2300-
.print(buffer);
2301-
if (decorator != TagTypeDecorator::None)
2302-
buffer << '>';
2303-
return;
2304-
}
2305-
}
2306-
} else if (arg.getKind() == clang::TemplateArgument::Integral) {
2307-
buffer << "_";
2308-
if (arg.getIntegralType()->isBuiltinType()) {
2309-
if (auto swiftTypeName = getSwiftBuiltinTypeName(
2310-
arg.getIntegralType()->getAs<clang::BuiltinType>())) {
2311-
buffer << *swiftTypeName << "_";
2312-
}
2313-
}
2314-
arg.getAsIntegral().print(buffer, true);
2315-
return;
2316-
}
2317-
buffer << "_";
2318-
});
2319-
buffer << ">";
2320-
2321-
baseName = swiftCtx.getIdentifier(buffer.str()).get();
2218+
auto name = printClassTemplateSpecializationName(classTemplateSpecDecl,
2219+
swiftCtx, this, version);
2220+
baseName = swiftCtx.getIdentifier(name).get();
23222221
}
23232222
}
23242223

test/Interop/Cxx/templates/Inputs/class-template-with-primitive-argument.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@ struct MagicWrapper {
88
};
99

1010
typedef MagicWrapper<int> WrappedMagicInt;
11+
typedef MagicWrapper<int*> WrappedMagicIntPtr;
12+
typedef MagicWrapper<const int*> WrappedMagicIntConstPtr;
13+
typedef MagicWrapper<int**> WrappedMagicIntPtrPtr;
1114

1215
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_CLASS_TEMPLATE_WITH_PRIMITIVE_ARGUMENT_H
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=ClassTemplateWithPrimitiveArgument -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
2+
3+
// CHECK: @available(*, unavailable
4+
// CHECK: struct MagicWrapper<T> {
5+
// CHECK: }
6+
7+
// CHECK: typealias WrappedMagicInt = MagicWrapper<CInt>
8+
// CHECK: typealias WrappedMagicIntPtr = MagicWrapper<UnsafeMutablePointer<CInt>>
9+
// CHECK: typealias WrappedMagicIntConstPtr = MagicWrapper<UnsafePointer<CInt>>
10+
// CHECK: typealias WrappedMagicIntPtrPtr = MagicWrapper<UnsafeMutablePointer<UnsafeMutablePointer<CInt>>>

0 commit comments

Comments
 (0)