[interop][SwiftToCxx] add support for passing and returning class values to generic functions

This commit is contained in:
Alex Lorenz
2022-08-08 08:38:28 -07:00
parent 38648ec209
commit a9f262e322
11 changed files with 166 additions and 20 deletions

View File

@@ -12,6 +12,7 @@
#include "ClangSyntaxPrinter.h"
#include "swift/ABI/MetadataValues.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Module.h"
using namespace swift;
@@ -168,3 +169,14 @@ void ClangSyntaxPrinter::printValueWitnessTableAccessSequenceFromTypeMetadata(
os << "auto *" << vwTableVariable << " = *vwTableAddr;\n";
os << "#endif\n";
}
void ClangSyntaxPrinter::printCTypeMetadataTypeFunction(
const NominalTypeDecl *typeDecl, StringRef typeMetadataFuncName) {
os << "// Type metadata accessor for " << typeDecl->getNameStr() << "\n";
os << "SWIFT_EXTERN ";
printSwiftImplQualifier();
os << "MetadataResponseTy " << typeMetadataFuncName << '(';
printSwiftImplQualifier();
os << "MetadataRequestTy)";
os << " SWIFT_NOEXCEPT SWIFT_CALL;\n\n";
}

View File

@@ -21,6 +21,7 @@
namespace swift {
class ModuleDecl;
class NominalTypeDecl;
namespace cxx_synthesis {
@@ -95,6 +96,10 @@ public:
void printValueWitnessTableAccessSequenceFromTypeMetadata(
StringRef metadataVariable, StringRef vwTableVariable, int indent);
/// Print the metadata accessor function for the given type declaration.
void printCTypeMetadataTypeFunction(const NominalTypeDecl *typeDecl,
StringRef typeMetadataFuncName);
protected:
raw_ostream &os;
};

View File

@@ -105,6 +105,7 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx,
"#include <cstring>\n";
out << "#include <stdlib.h>\n";
out << "#include <new>\n";
out << "#include <type_traits>\n";
// FIXME: Look for the header in the SDK.
out << "// Look for the C++ interop support header relative to clang's resource dir:\n";
out << "// '<toolchain>/usr/lib/clang/<version>/include/../../../swift/shims'.\n";

View File

@@ -14,6 +14,7 @@
#include "ClangSyntaxPrinter.h"
#include "PrintClangValueType.h"
#include "swift/AST/Decl.h"
#include "swift/IRGen/Linking.h"
using namespace swift;
@@ -23,12 +24,20 @@ void ClangClassTypePrinter::printClassTypeDecl(
ClangSyntaxPrinter printer(os);
auto typeMetadataFunc = irgen::LinkEntity::forTypeMetadataAccessFunction(
typeDecl->getDeclaredType()->getCanonicalType());
std::string typeMetadataFuncName = typeMetadataFunc.mangleAsString();
// Print out a forward declaration of the "hidden" _impl class.
printer.printNamespace(cxx_synthesis::getCxxImplNamespaceName(),
[&](raw_ostream &os) {
os << "class ";
printCxxImplClassName(os, typeDecl);
os << ";\n";
// Print out special functions, like functions that
// access type metadata.
printer.printCTypeMetadataTypeFunction(
typeDecl, typeMetadataFuncName);
});
std::string baseClassName;
@@ -84,6 +93,48 @@ void ClangClassTypePrinter::printClassTypeDecl(
os << "(ptr); }\n";
os << "};\n";
});
// FIXME: avoid popping out of the module's namespace here.
os << "} // end namespace \n\n";
os << "namespace swift {\n";
os << "#pragma clang diagnostic push\n";
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
os << "template<>\n";
os << "static inline const constexpr bool isUsableInGenericContext<";
printer.printBaseName(typeDecl->getModuleContext());
os << "::";
printer.printBaseName(typeDecl);
os << "> = true;\n";
os << "#pragma clang diagnostic pop\n";
os << "template<>\n";
os << "inline void * _Nonnull getTypeMetadata<";
printer.printBaseName(typeDecl->getModuleContext());
os << "::";
printer.printBaseName(typeDecl);
os << ">() {\n";
os << " return ";
printer.printBaseName(typeDecl->getModuleContext());
os << "::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::" << typeMetadataFuncName << "(0)._0;\n";
os << "}\n";
os << "namespace " << cxx_synthesis::getCxxImplNamespaceName() << "{\n";
os << "template<>\n";
os << "struct implClassFor<";
printer.printBaseName(typeDecl->getModuleContext());
os << "::";
printer.printBaseName(typeDecl);
os << "> { using type = ";
printer.printBaseName(typeDecl->getModuleContext());
os << "::" << cxx_synthesis::getCxxImplNamespaceName() << "::";
printCxxImplClassName(os, typeDecl);
os << "; };\n";
os << "} // namespace\n";
os << "} // namespace swift\n";
os << "\nnamespace ";
printer.printBaseName(typeDecl->getModuleContext());
os << " {\n";
}
void ClangClassTypePrinter::printClassTypeReturnScaffold(

View File

@@ -574,12 +574,23 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
!hasKnownOptionalNullableCxxMapping(resultTy)) {
if (isGenericType(resultTy)) {
// FIXME: Support returning value types.
os << " T returnValue;\n";
std::string returnAddress;
llvm::raw_string_ostream ros(returnAddress);
ros << "reinterpret_cast<void *>(&returnValue)";
os << " if constexpr (std::is_base_of<::swift::"
<< cxx_synthesis::getCxxImplNamespaceName()
<< "::RefCountedClass, T>::value) {\n";
os << " void *returnValue;\n ";
printCallToCFunc(/*additionalParam=*/StringRef(ros.str()));
os << ";\n";
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::implClassFor<T>::type::makeRetained(returnValue);\n";
os << " } else {\n";
os << " T returnValue;\n";
printCallToCFunc(/*additionalParam=*/StringRef(ros.str()));
os << ";\n return returnValue;\n";
os << " }\n";
return;
}
if (auto *classDecl = resultTy->getClassOrBoundGenericClass()) {

View File

@@ -80,19 +80,6 @@ printCValueTypeStorageStruct(raw_ostream &os, const NominalTypeDecl *typeDecl,
os << "};\n\n";
}
void printCTypeMetadataTypeFunction(raw_ostream &os,
const NominalTypeDecl *typeDecl,
StringRef typeMetadataFuncName) {
os << "// Type metadata accessor for " << typeDecl->getNameStr() << "\n";
os << "SWIFT_EXTERN ";
ClangSyntaxPrinter printer(os);
printer.printSwiftImplQualifier();
os << "MetadataResponseTy " << typeMetadataFuncName << '(';
printer.printSwiftImplQualifier();
os << "MetadataRequestTy)";
os << " SWIFT_NOEXCEPT SWIFT_CALL;\n\n";
}
void ClangValueTypePrinter::printValueTypeDecl(
const NominalTypeDecl *typeDecl,
llvm::function_ref<void(void)> bodyPrinter) {
@@ -124,8 +111,8 @@ void ClangValueTypePrinter::printValueTypeDecl(
// Print out special functions, like functions that
// access type metadata.
printCTypeMetadataTypeFunction(os, typeDecl,
typeMetadataFuncName);
printer.printCTypeMetadataTypeFunction(
typeDecl, typeMetadataFuncName);
});
auto printEnumVWTableVariable = [&](StringRef metadataName = "metadata",

View File

@@ -109,6 +109,16 @@ static inline const constexpr bool isUsableInGenericContext = false;
/// Returns the type metadat for the given Swift type T.
template <class T> inline void *_Nonnull getTypeMetadata();
namespace _impl {
/// Type trait that returns the `_impl::_impl_<T>` class type for the given
/// class T.
template <class T> struct implClassFor {
// using type = ...;
};
} // namespace _impl
} // namespace swift
#endif

View File

@@ -28,6 +28,9 @@ public final class ClassWithIntField {
// CHECK: namespace _impl {
// CHECK-EMPTY:
// CHECK-NEXT: class _impl_ClassWithIntField;
// CHECK-NEXT: // Type metadata accessor for ClassWithIntField
// CHECK-NEXT: SWIFT_EXTERN swift::_impl::MetadataResponseTy $s5Class0A12WithIntFieldCMa(swift::_impl::MetadataRequestTy) SWIFT_NOEXCEPT SWIFT_CALL;
// CHECK-EMPTY:
// CHECK-EMPTY:
// CHECK-NEXT: } // namespace _impl
// CHECK-EMPTY:
@@ -49,6 +52,26 @@ public final class ClassWithIntField {
// CHECK-NEXT:};
// CHECK-EMPTY:
// CHECK-NEXT:} // namespace _impl
// CHECK-EMPTY:
// CHECK-NEXT: } // end namespace
// CHECK-EMPTY:
// CHECK-NEXT: namespace swift {
// CHECK-NEXT: #pragma clang diagnostic push
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
// CHECK-NEXT: template<>
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Class::ClassWithIntField> = true;
// CHECK-NEXT: #pragma clang diagnostic pop
// CHECK-NEXT: template<>
// CHECK-NEXT: inline void * _Nonnull getTypeMetadata<Class::ClassWithIntField>() {
// CHECK-NEXT: return Class::_impl::$s5Class0A12WithIntFieldCMa(0)._0;
// CHECK-NEXT: }
// CHECK-NEXT: namespace _impl{
// CHECK-NEXT: template<>
// CHECK-NEXT: struct implClassFor<Class::ClassWithIntField> { using type = Class::_impl::_impl_ClassWithIntField; };
// CHECK-NEXT: } // namespace
// CHECK-NEXT: } // namespace swift
// CHECK-EMPTY:
// CHECK-NEXT: namespace Class {
// CHECK: inline ClassWithIntField passThroughClassWithIntField(const ClassWithIntField& x) noexcept SWIFT_WARN_UNUSED_RESULT {
// CHECK-NEXT: return _impl::_impl_ClassWithIntField::makeRetained(_impl::$s5Class011passThroughA12WithIntFieldyAA0adeF0CADF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(x)));

View File

@@ -126,5 +126,35 @@ int main() {
double x = -19.75;
assert(genericRet(x) == -19.75);
}
{
auto tc = createTestClass();
genericPrintFunction(tc);
}
// CHECK-NEXT: TestClass value=Functions.TestClass
// CHECK-NEXT: deinit TestClass
{
auto tc = createTestClass();
auto tc2 = genericRet(tc);
assert(swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc) ==
swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc2));
genericPrintFunction(tc2);
}
// CHECK-NEXT: TestClass value=Functions.TestClass
// CHECK-NEXT: deinit TestClass
{
auto tc = createTestClass();
auto tc2 = createTestClass();
const auto p1 = swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc);
const auto p2 = swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc2);
assert(p1 != p2);
genericSwap(tc, tc2);
assert(p2 == swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc));
assert(p1 == swift::_impl::_impl_RefCountedClass::getOpaquePointer(tc2));
}
// CHECK-NEXT: deinit TestClass
// CHECK-NEXT: deinit TestClass
return 0;
}

View File

@@ -31,6 +31,15 @@ public func genericRet<T>(_ x: T) -> T {
return x
}
public class TestClass {
let field: Int
init() { field = 0 }
deinit { print("deinit TestClass") }
}
public func createTestClass() -> TestClass { return TestClass() }
// CHECK: SWIFT_EXTERN void $s9Functions20genericPrintFunctionyyxlF(const void * _Nonnull x, void * _Nonnull ) SWIFT_NOEXCEPT SWIFT_CALL; // genericPrintFunction(_:)
// 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(_:_:_:_:_:)
// 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 {
// CHECK: template<class T>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T>
// CHECK-NEXT: inline T genericRet(const T & x) noexcept SWIFT_WARN_UNUSED_RESULT {
// CHECK-NEXT: T returnValue;
// CHECK-NEXT: _impl::$s9Functions10genericRetyxxlF(reinterpret_cast<void *>(&returnValue), reinterpret_cast<const void *>(&x), swift::getTypeMetadata<T>());
// CHECK-NEXT: return returnValue;
// CHECK-NEXT: }
// CHECK-NEXT: if constexpr (std::is_base_of<::swift::_impl::RefCountedClass, T>::value) {
// CHECK-NEXT: void *returnValue;
// CHECK-NEXT: _impl::$s9Functions10genericRetyxxlF(reinterpret_cast<void *>(&returnValue), reinterpret_cast<const void *>(&x), swift::getTypeMetadata<T>());
// CHECK-NEXT: return ::swift::_impl::implClassFor<T>::type::makeRetained(returnValue);
// CHECK-NEXT: } else {
// CHECK-NEXT: T returnValue;
// CHECK-NEXT: _impl::$s9Functions10genericRetyxxlF(reinterpret_cast<void *>(&returnValue), reinterpret_cast<const void *>(&x), swift::getTypeMetadata<T>());
// CHECK-NEXT: return returnValue;
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK: template<class T>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T>

View File

@@ -31,6 +31,7 @@
// CHECK-NEXT: #include <cstring>
// CHECK-NEXT: #include <stdlib.h>
// CHECK-NEXT: #include <new>
// CHECK-NEXT: #include <type_traits>
// CHECK-NEXT: // Look for the C++ interop support header relative to clang's resource dir:
// CHECK-NEXT: // '<toolchain>/usr/lib/clang/<version>/include/../../../swift/shims'.
// CHECK-NEXT: #if __has_include(<../../../swift/shims/_SwiftCxxInteroperability.h>)