Skip to content

Commit 09c373f

Browse files
committed
[cxx-interop] Disambiguate template instantiations with parameter packs
This makes sure that different template instantiations of `std::tuple` get distinct Swift type names. Similar to aa6804a. This also refactors `swift::importer::printClassTemplateSpecializationName` to follow a proper visitor pattern for the C++ template arguments. rdar://139435937
1 parent 108ad6f commit 09c373f

File tree

4 files changed

+109
-31
lines changed

4 files changed

+109
-31
lines changed

lib/ClangImporter/ClangClassTemplateNamePrinter.cpp

Lines changed: 93 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "ClangClassTemplateNamePrinter.h"
1414
#include "ImporterImpl.h"
15+
#include "clang/AST/TemplateArgumentVisitor.h"
1516
#include "clang/AST/TypeVisitor.h"
1617

1718
using namespace swift;
@@ -150,41 +151,106 @@ struct TemplateInstantiationNamePrinter
150151
}
151152
};
152153

154+
struct TemplateArgumentPrinter
155+
: clang::ConstTemplateArgumentVisitor<TemplateArgumentPrinter, void,
156+
llvm::raw_svector_ostream &> {
157+
TemplateInstantiationNamePrinter typePrinter;
158+
159+
TemplateArgumentPrinter(ASTContext &swiftCtx, NameImporter *nameImporter,
160+
ImportNameVersion version)
161+
: typePrinter(swiftCtx, nameImporter, version) {}
162+
163+
void VisitTemplateArgument(const clang::TemplateArgument &arg,
164+
llvm::raw_svector_ostream &buffer) {
165+
// Print "_" as a fallback if we couldn't emit a more meaningful type name.
166+
buffer << "_";
167+
}
168+
169+
void VisitTypeTemplateArgument(const clang::TemplateArgument &arg,
170+
llvm::raw_svector_ostream &buffer) {
171+
auto ty = arg.getAsType();
172+
buffer << typePrinter.Visit(ty.getTypePtr());
173+
if (ty.isConstQualified()) {
174+
buffer << "_const";
175+
}
176+
}
177+
178+
void VisitIntegralTemplateArgument(const clang::TemplateArgument &arg,
179+
llvm::raw_svector_ostream &buffer) {
180+
buffer << "_";
181+
if (arg.getIntegralType()->isBuiltinType()) {
182+
buffer << typePrinter.Visit(arg.getIntegralType().getTypePtr()) << "_";
183+
}
184+
arg.getAsIntegral().print(buffer, true);
185+
}
186+
187+
void VisitPackTemplateArgument(const clang::TemplateArgument &arg,
188+
llvm::raw_svector_ostream &buffer) {
189+
VisitTemplateArgumentArray(arg.getPackAsArray(), buffer);
190+
}
191+
192+
void VisitTemplateArgumentArray(ArrayRef<clang::TemplateArgument> args,
193+
llvm::raw_svector_ostream &buffer) {
194+
bool needsComma = false;
195+
for (auto &arg : args) {
196+
// Do not try to print empty packs.
197+
if (arg.getKind() == clang::TemplateArgument::ArgKind::Pack &&
198+
arg.getPackAsArray().empty())
199+
continue;
200+
201+
if (needsComma)
202+
buffer << ", ";
203+
Visit(arg, buffer);
204+
needsComma = true;
205+
}
206+
}
207+
208+
// Unfortunately, clang::templateargumentvisitor::Base does not correctly
209+
// perform static dispatch for methods that aren't overloaded, so we have to
210+
// overload all of them here.
211+
212+
void VisitNullArgument(const clang::TemplateArgument &arg,
213+
llvm::raw_svector_ostream &buffer) {
214+
VisitTemplateArgument(arg, buffer);
215+
}
216+
void VisitDeclarationArgument(const clang::TemplateArgument &arg,
217+
llvm::raw_svector_ostream &buffer) {
218+
VisitTemplateArgument(arg, buffer);
219+
}
220+
void VisitNullPtrArgument(const clang::TemplateArgument &arg,
221+
llvm::raw_svector_ostream &buffer) {
222+
VisitTemplateArgument(arg, buffer);
223+
}
224+
void VisitStructuralValueArgument(const clang::TemplateArgument &arg,
225+
llvm::raw_svector_ostream &buffer) {
226+
VisitTemplateArgument(arg, buffer);
227+
}
228+
void VisitTemplateTemplateArgument(const clang::TemplateArgument &arg,
229+
llvm::raw_svector_ostream &buffer) {
230+
VisitTemplateArgument(arg, buffer);
231+
}
232+
void
233+
VisitTemplateExpansionTemplateArgument(const clang::TemplateArgument &arg,
234+
llvm::raw_svector_ostream &buffer) {
235+
VisitTemplateArgument(arg, buffer);
236+
}
237+
void VisitExpressionTemplateArgument(const clang::TemplateArgument &arg,
238+
llvm::raw_svector_ostream &buffer) {
239+
VisitTemplateArgument(arg, buffer);
240+
}
241+
};
242+
153243
std::string swift::importer::printClassTemplateSpecializationName(
154244
const clang::ClassTemplateSpecializationDecl *decl, ASTContext &swiftCtx,
155245
NameImporter *nameImporter, ImportNameVersion version) {
156-
TemplateInstantiationNamePrinter templateNamePrinter(swiftCtx, nameImporter,
157-
version);
246+
TemplateArgumentPrinter templateArgPrinter(swiftCtx, nameImporter, version);
158247

159-
// TODO: the following logic should probably be a ConstTemplateArgumentVisitor
160248
llvm::SmallString<128> storage;
161249
llvm::raw_svector_ostream buffer(storage);
162250
decl->printName(buffer);
163251
buffer << "<";
164-
llvm::interleaveComma(
165-
decl->getTemplateArgs().asArray(), buffer,
166-
[&buffer, &templateNamePrinter](const clang::TemplateArgument &arg) {
167-
// Use import name here so builtin types such as "int" map to their
168-
// Swift equivalent ("CInt").
169-
if (arg.getKind() == clang::TemplateArgument::Type) {
170-
auto ty = arg.getAsType();
171-
buffer << templateNamePrinter.Visit(ty.getTypePtr());
172-
if (ty.isConstQualified()) {
173-
buffer << "_const";
174-
}
175-
return;
176-
} else if (arg.getKind() == clang::TemplateArgument::Integral) {
177-
buffer << "_";
178-
if (arg.getIntegralType()->isBuiltinType()) {
179-
buffer << templateNamePrinter.Visit(
180-
arg.getIntegralType().getTypePtr())
181-
<< "_";
182-
}
183-
arg.getAsIntegral().print(buffer, true);
184-
return;
185-
}
186-
buffer << "_";
187-
});
252+
templateArgPrinter.VisitTemplateArgumentArray(
253+
decl->getTemplateArgs().asArray(), buffer);
188254
buffer << ">";
189255
return buffer.str().str();
190256
}

test/Interop/Cxx/templates/Inputs/class-template-variadic.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ struct IntWrapper {
2525
int getValue() const { return value; }
2626
};
2727

28+
typedef Tuple<IntWrapper> Single;
2829
typedef Tuple<IntWrapper, IntWrapper> Pair;
30+
typedef Tuple<IntWrapper, IntWrapper, IntWrapper> Triple;
31+
typedef Tuple<Tuple<IntWrapper, IntWrapper>, Tuple<IntWrapper, IntWrapper>> Nested;
2932

3033
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_CLASS_TEMPLATE_VARIADIC_H
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
// RUN: %target-swift-ide-test -print-module -module-to-print=ClassTemplateInNamespace -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
22

33
// CHECK: enum Space {
4-
// CHECK: struct Ship<_> {
5-
// CHECK: init()
6-
// CHECK: }
74
// CHECK: @available(*, unavailable, message: "Un-specialized class templates are not currently supported. Please use a specialization of this type.")
85
// CHECK: struct Ship<> {
96
// CHECK: }
10-
// CHECK: typealias Orbiter = Space.Ship<_>
7+
// CHECK: typealias Orbiter = Space.Ship<((CBool) -> Void)>
118
// CHECK: }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=ClassTemplateVariadic -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
2+
// RUN: %target-swift-ide-test -print-module -module-to-print=ClassTemplateVariadic -I %S/Inputs -source-filename=x -cxx-interoperability-mode=swift-6 | %FileCheck %s
3+
// RUN: %target-swift-ide-test -print-module -module-to-print=ClassTemplateVariadic -I %S/Inputs -source-filename=x -cxx-interoperability-mode=upcoming-swift | %FileCheck %s
4+
5+
// CHECK: @available(*, unavailable
6+
// CHECK: struct Tuple<Ts> {
7+
// CHECK: }
8+
9+
// CHECK: typealias Single = Tuple<IntWrapper>
10+
// CHECK: typealias Pair = Tuple<IntWrapper, IntWrapper>
11+
// CHECK: typealias Triple = Tuple<IntWrapper, IntWrapper, IntWrapper>
12+
// CHECK: typealias Nested = Tuple<Tuple<IntWrapper, IntWrapper>, Tuple<IntWrapper, IntWrapper>>

0 commit comments

Comments
 (0)