mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[interop][SwiftToCxx] add support for passing and returning class values to generic functions
This commit is contained in:
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>)
|
||||
|
||||
Reference in New Issue
Block a user