Skip to content

[interop][SwiftToCxx] add support for passing and returning class val… #60442

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/PrintAsClang/ClangSyntaxPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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";
}
5 changes: 5 additions & 0 deletions lib/PrintAsClang/ClangSyntaxPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
namespace swift {

class ModuleDecl;
class NominalTypeDecl;

namespace cxx_synthesis {

Expand Down Expand Up @@ -95,6 +96,10 @@ class ClangSyntaxPrinter {
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;
};
Expand Down
1 change: 1 addition & 0 deletions lib/PrintAsClang/PrintAsClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
51 changes: 51 additions & 0 deletions lib/PrintAsClang/PrintClangClassType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "ClangSyntaxPrinter.h"
#include "PrintClangValueType.h"
#include "swift/AST/Decl.h"
#include "swift/IRGen/Linking.h"

using namespace swift;

Expand All @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
13 changes: 12 additions & 1 deletion lib/PrintAsClang/PrintClangFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
17 changes: 2 additions & 15 deletions lib/PrintAsClang/PrintClangValueType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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",
Expand Down
10 changes: 10 additions & 0 deletions stdlib/public/SwiftShims/_SwiftCxxInteroperability.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
23 changes: 23 additions & 0 deletions test/Interop/SwiftToCxx/class/swift-class-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)));
Expand Down
30 changes: 30 additions & 0 deletions test/Interop/SwiftToCxx/generics/generic-function-execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
23 changes: 19 additions & 4 deletions test/Interop/SwiftToCxx/generics/generic-function-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(_:_:)
Expand Down Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions test/PrintAsCxx/empty.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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>)
Expand Down