Skip to content

Commit 7afa4cf

Browse files
authored
Merge pull request #69408 from apple/egorzhdan/deep-template-mangling
[cxx-interop] Use unique mangling for distinct C++ class template specializations
2 parents ff6ee74 + a45d03a commit 7afa4cf

File tree

7 files changed

+209
-109
lines changed

7 files changed

+209
-109
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>>>

test/SILGen/opaque_values_cxx.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ import Cxx
1515
// CHECK: end_borrow [[VECTOR]]
1616
// CHECK: return [[BEGIN]]
1717
// CHECK-LABEL: } // end sil function '$sSo3stdO3__1O0055vectorCUnsignedIntallocatorCUnsignedInt_iqGBpboaivxaEhaV3Cxx0B8SequenceSCAgHP13__beginUnsafe11RawIteratorQzyFTW'
18-
// CHECK-LABEL: sil {{.*}}[ossa] @$sSo3stdO{{(3__1O)?}}0020___wrap_iter__udAAdDaVSQSCSQ2eeoiySbx_xtFZTW : {{.*}} {
19-
// CHECK: bb0([[LHS:%[^,]+]] : $std.__1.__wrap_iter<_>, [[RHS:%[^,]+]] :
20-
// CHECK: [[CALLEE:%[^,]+]] = function_ref @$sSo2eeoiySbSo3stdO{{(3__1O)?}}0020___wrap_iter__udAAdDaV_AGtFTO
18+
// CHECK-LABEL: sil {{.*}}[ossa] @$sSo3stdO{{(3__1O)?}}0047___wrap_iterUnsafePointerCUnsignedInt_heCInnaEgaVSQSCSQ2eeoiySbx_xtFZTW : {{.*}} {
19+
// CHECK: bb0([[LHS:%[^,]+]] : $std.__1.__wrap_iter<UnsafePointer<CUnsignedInt>>, [[RHS:%[^,]+]] :
20+
// CHECK: [[CALLEE:%[^,]+]] = function_ref @$sSo2eeoiySbSo3stdO{{(3__1O)?}}0047___wrap_iterUnsafePointerCUnsignedInt_heCInnaEgaV_AGtFTO
2121
// CHECK: [[EQUAL:%[^,]+]] = apply [[CALLEE]]([[LHS]], [[RHS]])
2222
// CHECK: return [[EQUAL]]
23-
// CHECK-LABEL: } // end sil function '$sSo3stdO{{(3__1O)?}}0020___wrap_iter__udAAdDaVSQSCSQ2eeoiySbx_xtFZTW'
23+
// CHECK-LABEL: } // end sil function '$sSo3stdO{{(3__1O)?}}0047___wrap_iterUnsafePointerCUnsignedInt_heCInnaEgaVSQSCSQ2eeoiySbx_xtFZTW'
2424
func test_cxx_vector_uint32t_iterate(_ n: Int, _ vectorOfU32: VectorOfU32) {
2525
for x in vectorOfU32 {}
2626
}

0 commit comments

Comments
 (0)