Skip to content

Commit bb7ba7c

Browse files
authored
Merge pull request swiftlang#60442 from hyp/eng/generic-class
[interop][SwiftToCxx] add support for passing and returning class val…
2 parents 8898b5a + a9f262e commit bb7ba7c

File tree

11 files changed

+166
-20
lines changed

11 files changed

+166
-20
lines changed

lib/PrintAsClang/ClangSyntaxPrinter.cpp

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

1313
#include "ClangSyntaxPrinter.h"
1414
#include "swift/ABI/MetadataValues.h"
15+
#include "swift/AST/Decl.h"
1516
#include "swift/AST/Module.h"
1617

1718
using namespace swift;
@@ -168,3 +169,14 @@ void ClangSyntaxPrinter::printValueWitnessTableAccessSequenceFromTypeMetadata(
168169
os << "auto *" << vwTableVariable << " = *vwTableAddr;\n";
169170
os << "#endif\n";
170171
}
172+
173+
void ClangSyntaxPrinter::printCTypeMetadataTypeFunction(
174+
const NominalTypeDecl *typeDecl, StringRef typeMetadataFuncName) {
175+
os << "// Type metadata accessor for " << typeDecl->getNameStr() << "\n";
176+
os << "SWIFT_EXTERN ";
177+
printSwiftImplQualifier();
178+
os << "MetadataResponseTy " << typeMetadataFuncName << '(';
179+
printSwiftImplQualifier();
180+
os << "MetadataRequestTy)";
181+
os << " SWIFT_NOEXCEPT SWIFT_CALL;\n\n";
182+
}

lib/PrintAsClang/ClangSyntaxPrinter.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
namespace swift {
2222

2323
class ModuleDecl;
24+
class NominalTypeDecl;
2425

2526
namespace cxx_synthesis {
2627

@@ -95,6 +96,10 @@ class ClangSyntaxPrinter {
9596
void printValueWitnessTableAccessSequenceFromTypeMetadata(
9697
StringRef metadataVariable, StringRef vwTableVariable, int indent);
9798

99+
/// Print the metadata accessor function for the given type declaration.
100+
void printCTypeMetadataTypeFunction(const NominalTypeDecl *typeDecl,
101+
StringRef typeMetadataFuncName);
102+
98103
protected:
99104
raw_ostream &os;
100105
};

lib/PrintAsClang/PrintAsClang.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx,
105105
"#include <cstring>\n";
106106
out << "#include <stdlib.h>\n";
107107
out << "#include <new>\n";
108+
out << "#include <type_traits>\n";
108109
// FIXME: Look for the header in the SDK.
109110
out << "// Look for the C++ interop support header relative to clang's resource dir:\n";
110111
out << "// '<toolchain>/usr/lib/clang/<version>/include/../../../swift/shims'.\n";

lib/PrintAsClang/PrintClangClassType.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "ClangSyntaxPrinter.h"
1515
#include "PrintClangValueType.h"
1616
#include "swift/AST/Decl.h"
17+
#include "swift/IRGen/Linking.h"
1718

1819
using namespace swift;
1920

@@ -23,12 +24,20 @@ void ClangClassTypePrinter::printClassTypeDecl(
2324

2425
ClangSyntaxPrinter printer(os);
2526

27+
auto typeMetadataFunc = irgen::LinkEntity::forTypeMetadataAccessFunction(
28+
typeDecl->getDeclaredType()->getCanonicalType());
29+
std::string typeMetadataFuncName = typeMetadataFunc.mangleAsString();
30+
2631
// Print out a forward declaration of the "hidden" _impl class.
2732
printer.printNamespace(cxx_synthesis::getCxxImplNamespaceName(),
2833
[&](raw_ostream &os) {
2934
os << "class ";
3035
printCxxImplClassName(os, typeDecl);
3136
os << ";\n";
37+
// Print out special functions, like functions that
38+
// access type metadata.
39+
printer.printCTypeMetadataTypeFunction(
40+
typeDecl, typeMetadataFuncName);
3241
});
3342

3443
std::string baseClassName;
@@ -84,6 +93,48 @@ void ClangClassTypePrinter::printClassTypeDecl(
8493
os << "(ptr); }\n";
8594
os << "};\n";
8695
});
96+
97+
// FIXME: avoid popping out of the module's namespace here.
98+
os << "} // end namespace \n\n";
99+
os << "namespace swift {\n";
100+
101+
os << "#pragma clang diagnostic push\n";
102+
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
103+
os << "template<>\n";
104+
os << "static inline const constexpr bool isUsableInGenericContext<";
105+
printer.printBaseName(typeDecl->getModuleContext());
106+
os << "::";
107+
printer.printBaseName(typeDecl);
108+
os << "> = true;\n";
109+
os << "#pragma clang diagnostic pop\n";
110+
111+
os << "template<>\n";
112+
os << "inline void * _Nonnull getTypeMetadata<";
113+
printer.printBaseName(typeDecl->getModuleContext());
114+
os << "::";
115+
printer.printBaseName(typeDecl);
116+
os << ">() {\n";
117+
os << " return ";
118+
printer.printBaseName(typeDecl->getModuleContext());
119+
os << "::" << cxx_synthesis::getCxxImplNamespaceName()
120+
<< "::" << typeMetadataFuncName << "(0)._0;\n";
121+
os << "}\n";
122+
os << "namespace " << cxx_synthesis::getCxxImplNamespaceName() << "{\n";
123+
os << "template<>\n";
124+
os << "struct implClassFor<";
125+
printer.printBaseName(typeDecl->getModuleContext());
126+
os << "::";
127+
printer.printBaseName(typeDecl);
128+
os << "> { using type = ";
129+
printer.printBaseName(typeDecl->getModuleContext());
130+
os << "::" << cxx_synthesis::getCxxImplNamespaceName() << "::";
131+
printCxxImplClassName(os, typeDecl);
132+
os << "; };\n";
133+
os << "} // namespace\n";
134+
os << "} // namespace swift\n";
135+
os << "\nnamespace ";
136+
printer.printBaseName(typeDecl->getModuleContext());
137+
os << " {\n";
87138
}
88139

89140
void ClangClassTypePrinter::printClassTypeReturnScaffold(

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,12 +576,23 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
576576
!hasKnownOptionalNullableCxxMapping(resultTy)) {
577577
if (isGenericType(resultTy)) {
578578
// FIXME: Support returning value types.
579-
os << " T returnValue;\n";
580579
std::string returnAddress;
581580
llvm::raw_string_ostream ros(returnAddress);
582581
ros << "reinterpret_cast<void *>(&returnValue)";
582+
583+
os << " if constexpr (std::is_base_of<::swift::"
584+
<< cxx_synthesis::getCxxImplNamespaceName()
585+
<< "::RefCountedClass, T>::value) {\n";
586+
os << " void *returnValue;\n ";
587+
printCallToCFunc(/*additionalParam=*/StringRef(ros.str()));
588+
os << ";\n";
589+
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
590+
<< "::implClassFor<T>::type::makeRetained(returnValue);\n";
591+
os << " } else {\n";
592+
os << " T returnValue;\n";
583593
printCallToCFunc(/*additionalParam=*/StringRef(ros.str()));
584594
os << ";\n return returnValue;\n";
595+
os << " }\n";
585596
return;
586597
}
587598
if (auto *classDecl = resultTy->getClassOrBoundGenericClass()) {

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,6 @@ printCValueTypeStorageStruct(raw_ostream &os, const NominalTypeDecl *typeDecl,
8080
os << "};\n\n";
8181
}
8282

83-
void printCTypeMetadataTypeFunction(raw_ostream &os,
84-
const NominalTypeDecl *typeDecl,
85-
StringRef typeMetadataFuncName) {
86-
os << "// Type metadata accessor for " << typeDecl->getNameStr() << "\n";
87-
os << "SWIFT_EXTERN ";
88-
ClangSyntaxPrinter printer(os);
89-
printer.printSwiftImplQualifier();
90-
os << "MetadataResponseTy " << typeMetadataFuncName << '(';
91-
printer.printSwiftImplQualifier();
92-
os << "MetadataRequestTy)";
93-
os << " SWIFT_NOEXCEPT SWIFT_CALL;\n\n";
94-
}
95-
9683
void ClangValueTypePrinter::printValueTypeDecl(
9784
const NominalTypeDecl *typeDecl,
9885
llvm::function_ref<void(void)> bodyPrinter) {
@@ -124,8 +111,8 @@ void ClangValueTypePrinter::printValueTypeDecl(
124111

125112
// Print out special functions, like functions that
126113
// access type metadata.
127-
printCTypeMetadataTypeFunction(os, typeDecl,
128-
typeMetadataFuncName);
114+
printer.printCTypeMetadataTypeFunction(
115+
typeDecl, typeMetadataFuncName);
129116
});
130117

131118
auto printEnumVWTableVariable = [&](StringRef metadataName = "metadata",

stdlib/public/SwiftShims/_SwiftCxxInteroperability.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,16 @@ static inline const constexpr bool isUsableInGenericContext = false;
109109
/// Returns the type metadat for the given Swift type T.
110110
template <class T> inline void *_Nonnull getTypeMetadata();
111111

112+
namespace _impl {
113+
114+
/// Type trait that returns the `_impl::_impl_<T>` class type for the given
115+
/// class T.
116+
template <class T> struct implClassFor {
117+
// using type = ...;
118+
};
119+
120+
} // namespace _impl
121+
112122
} // namespace swift
113123
#endif
114124

test/Interop/SwiftToCxx/class/swift-class-in-cxx.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public final class ClassWithIntField {
2828
// CHECK: namespace _impl {
2929
// CHECK-EMPTY:
3030
// CHECK-NEXT: class _impl_ClassWithIntField;
31+
// CHECK-NEXT: // Type metadata accessor for ClassWithIntField
32+
// CHECK-NEXT: SWIFT_EXTERN swift::_impl::MetadataResponseTy $s5Class0A12WithIntFieldCMa(swift::_impl::MetadataRequestTy) SWIFT_NOEXCEPT SWIFT_CALL;
33+
// CHECK-EMPTY:
3134
// CHECK-EMPTY:
3235
// CHECK-NEXT: } // namespace _impl
3336
// CHECK-EMPTY:
@@ -49,6 +52,26 @@ public final class ClassWithIntField {
4952
// CHECK-NEXT:};
5053
// CHECK-EMPTY:
5154
// CHECK-NEXT:} // namespace _impl
55+
// CHECK-EMPTY:
56+
// CHECK-NEXT: } // end namespace
57+
// CHECK-EMPTY:
58+
// CHECK-NEXT: namespace swift {
59+
// CHECK-NEXT: #pragma clang diagnostic push
60+
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
61+
// CHECK-NEXT: template<>
62+
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Class::ClassWithIntField> = true;
63+
// CHECK-NEXT: #pragma clang diagnostic pop
64+
// CHECK-NEXT: template<>
65+
// CHECK-NEXT: inline void * _Nonnull getTypeMetadata<Class::ClassWithIntField>() {
66+
// CHECK-NEXT: return Class::_impl::$s5Class0A12WithIntFieldCMa(0)._0;
67+
// CHECK-NEXT: }
68+
// CHECK-NEXT: namespace _impl{
69+
// CHECK-NEXT: template<>
70+
// CHECK-NEXT: struct implClassFor<Class::ClassWithIntField> { using type = Class::_impl::_impl_ClassWithIntField; };
71+
// CHECK-NEXT: } // namespace
72+
// CHECK-NEXT: } // namespace swift
73+
// CHECK-EMPTY:
74+
// CHECK-NEXT: namespace Class {
5275

5376
// CHECK: inline ClassWithIntField passThroughClassWithIntField(const ClassWithIntField& x) noexcept SWIFT_WARN_UNUSED_RESULT {
5477
// CHECK-NEXT: return _impl::_impl_ClassWithIntField::makeRetained(_impl::$s5Class011passThroughA12WithIntFieldyAA0adeF0CADF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(x)));

test/Interop/SwiftToCxx/generics/generic-function-execution.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,5 +126,35 @@ int main() {
126126
double x = -19.75;
127127
assert(genericRet(x) == -19.75);
128128
}
129+
130+
{
131+
auto tc = createTestClass();
132+
genericPrintFunction(tc);
133+
}
134+
// CHECK-NEXT: TestClass value=Functions.TestClass
135+
// CHECK-NEXT: deinit TestClass
136+
137+
{
138+
auto tc = createTestClass();
139+
auto tc2 = genericRet(tc);
140+
assert(swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc) ==
141+
swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc2));
142+
genericPrintFunction(tc2);
143+
}
144+
// CHECK-NEXT: TestClass value=Functions.TestClass
145+
// CHECK-NEXT: deinit TestClass
146+
147+
{
148+
auto tc = createTestClass();
149+
auto tc2 = createTestClass();
150+
const auto p1 = swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc);
151+
const auto p2 = swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc2);
152+
assert(p1 != p2);
153+
genericSwap(tc, tc2);
154+
assert(p2 == swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc));
155+
assert(p1 == swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc2));
156+
}
157+
// CHECK-NEXT: deinit TestClass
158+
// CHECK-NEXT: deinit TestClass
129159
return 0;
130160
}

test/Interop/SwiftToCxx/generics/generic-function-in-cxx.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ public func genericRet<T>(_ x: T) -> T {
3131
return x
3232
}
3333

34+
public class TestClass {
35+
let field: Int
36+
37+
init() { field = 0 }
38+
deinit { print("deinit TestClass") }
39+
}
40+
41+
public func createTestClass() -> TestClass { return TestClass() }
42+
3443
// CHECK: SWIFT_EXTERN void $s9Functions20genericPrintFunctionyyxlF(const void * _Nonnull x, void * _Nonnull ) SWIFT_NOEXCEPT SWIFT_CALL; // genericPrintFunction(_:)
3544
// CHECK-NEXT: SWIFT_EXTERN void $s9Functions32genericPrintFunctionMultiGenericyySi_xxSiq_tr0_lF(ptrdiff_t x, const void * _Nonnull t1, const void * _Nonnull t1p, ptrdiff_t y, const void * _Nonnull t2, void * _Nonnull , void * _Nonnull ) SWIFT_NOEXCEPT SWIFT_CALL; // genericPrintFunctionMultiGeneric(_:_:_:_:_:)
3645
// CHECK-NEXT: SWIFT_EXTERN void $s9Functions26genericPrintFunctionTwoArgyyx_SitlF(const void * _Nonnull x, ptrdiff_t y, void * _Nonnull ) SWIFT_NOEXCEPT SWIFT_CALL; // genericPrintFunctionTwoArg(_:_:)
@@ -60,10 +69,16 @@ public func genericRet<T>(_ x: T) -> T {
6069
// CHECK: template<class T>
6170
// CHECK-NEXT: requires swift::isUsableInGenericContext<T>
6271
// CHECK-NEXT: inline T genericRet(const T & x) noexcept SWIFT_WARN_UNUSED_RESULT {
63-
// CHECK-NEXT: T returnValue;
64-
// CHECK-NEXT: _impl::$s9Functions10genericRetyxxlF(reinterpret_cast<void *>(&returnValue), reinterpret_cast<const void *>(&x), swift::getTypeMetadata<T>());
65-
// CHECK-NEXT: return returnValue;
66-
// CHECK-NEXT: }
72+
// CHECK-NEXT: if constexpr (std::is_base_of<::swift::_impl::RefCountedClass, T>::value) {
73+
// CHECK-NEXT: void *returnValue;
74+
// CHECK-NEXT: _impl::$s9Functions10genericRetyxxlF(reinterpret_cast<void *>(&returnValue), reinterpret_cast<const void *>(&x), swift::getTypeMetadata<T>());
75+
// CHECK-NEXT: return ::swift::_impl::implClassFor<T>::type::makeRetained(returnValue);
76+
// CHECK-NEXT: } else {
77+
// CHECK-NEXT: T returnValue;
78+
// CHECK-NEXT: _impl::$s9Functions10genericRetyxxlF(reinterpret_cast<void *>(&returnValue), reinterpret_cast<const void *>(&x), swift::getTypeMetadata<T>());
79+
// CHECK-NEXT: return returnValue;
80+
// CHECK-NEXT: }
81+
// CHECK-NEXT: }
6782

6883
// CHECK: template<class T>
6984
// CHECK-NEXT: requires swift::isUsableInGenericContext<T>

test/PrintAsCxx/empty.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
// CHECK-NEXT: #include <cstring>
3232
// CHECK-NEXT: #include <stdlib.h>
3333
// CHECK-NEXT: #include <new>
34+
// CHECK-NEXT: #include <type_traits>
3435
// CHECK-NEXT: // Look for the C++ interop support header relative to clang's resource dir:
3536
// CHECK-NEXT: // '<toolchain>/usr/lib/clang/<version>/include/../../../swift/shims'.
3637
// CHECK-NEXT: #if __has_include(<../../../swift/shims/_SwiftCxxInteroperability.h>)

0 commit comments

Comments
 (0)