[interop][SwiftToCxx] start emitting bindings for Swift class types

This includes release on destruction, and correctly returning class values from Swift to C++.
This commit is contained in:
Alex Lorenz
2022-08-01 20:03:56 +01:00
parent 700ef38353
commit 26d55a2b83
9 changed files with 262 additions and 3 deletions

View File

@@ -5,6 +5,7 @@ add_swift_host_library(swiftPrintAsClang STATIC
ModuleContentsWriter.cpp
PrimitiveTypeMapping.cpp
PrintAsClang.cpp
PrintClangClassType.cpp
PrintClangFunction.cpp
PrintClangValueType.cpp
PrintSwiftToClangCoreScaffold.cpp

View File

@@ -13,6 +13,7 @@
#include "DeclAndTypePrinter.h"
#include "ClangSyntaxPrinter.h"
#include "PrimitiveTypeMapping.h"
#include "PrintClangClassType.h"
#include "PrintClangFunction.h"
#include "PrintClangValueType.h"
#include "SwiftToClangInteropContext.h"
@@ -296,6 +297,15 @@ private:
void visitClassDecl(ClassDecl *CD) {
printDocumentationComment(CD);
if (outputLang == OutputLanguageMode::Cxx) {
// FIXME: Non objc class.
// FIXME: Print availability.
ClangClassTypePrinter(os).printClassTypeDecl(CD, []() {
// FIXME: print class body.
});
return;
}
// This is just for testing, so we check explicitly for the attribute instead
// of asking if the class is weak imported. If the class has availability,
// we'll print a SWIFT_AVAILABLE() which implies __attribute__((weak_imported))

View File

@@ -605,6 +605,8 @@ public:
if (auto ED = dyn_cast<EnumDecl>(D)) {
success = writeEnum(ED);
} else if (auto CD = dyn_cast<ClassDecl>(D)) {
success = writeClass(CD);
} else if (outputLangMode == OutputLanguageMode::Cxx) {
if (auto FD = dyn_cast<FuncDecl>(D))
success = writeFunc(FD);
@@ -612,9 +614,7 @@ public:
success = writeStruct(SD);
// FIXME: Warn on unsupported exported decl.
} else if (isa<ValueDecl>(D)) {
if (auto CD = dyn_cast<ClassDecl>(D))
success = writeClass(CD);
else if (auto PD = dyn_cast<ProtocolDecl>(D))
if (auto PD = dyn_cast<ProtocolDecl>(D))
success = writeProtocol(PD);
else if (auto ED = dyn_cast<FuncDecl>(D))
success = writeFunc(ED);

View File

@@ -0,0 +1,91 @@
//===--- PrintClangClassType.cpp - Print class types in C/C++ ---*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "PrintClangClassType.h"
#include "ClangSyntaxPrinter.h"
#include "PrintClangValueType.h"
#include "swift/AST/Decl.h"
using namespace swift;
void ClangClassTypePrinter::printClassTypeDecl(
const ClassDecl *typeDecl, llvm::function_ref<void(void)> bodyPrinter) {
auto printCxxImplClassName = ClangValueTypePrinter::printCxxImplClassName;
ClangSyntaxPrinter printer(os);
// 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";
});
os << "class ";
printer.printBaseName(typeDecl);
// FIXME: Add support for inherintance.
os << " final";
os << " {\n";
os << "public:\n";
// Destructor releases the object.
os << " inline ~";
printer.printBaseName(typeDecl);
os << "() { swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::swift_release(_opaquePointer); }\n";
// FIXME: move semantics should be restricted?
os << " inline ";
printer.printBaseName(typeDecl);
os << "(";
printer.printBaseName(typeDecl);
os << "&&) noexcept = default;\n";
os << "private:\n";
os << " inline ";
printer.printBaseName(typeDecl);
os << "(void * _Nonnull ptr) noexcept : _opaquePointer(ptr) {}\n";
os << "\n void * _Nonnull _opaquePointer;\n";
os << " friend class " << cxx_synthesis::getCxxImplNamespaceName() << "::";
printCxxImplClassName(os, typeDecl);
os << ";\n";
os << "};\n\n";
// Print out the "hidden" _impl class.
printer.printNamespace(
cxx_synthesis::getCxxImplNamespaceName(), [&](raw_ostream &os) {
os << "class ";
printCxxImplClassName(os, typeDecl);
os << " {\n";
os << "public:\n";
os << "static inline ";
printer.printBaseName(typeDecl);
os << " fromUnretained(void * _Nonnull ptr) noexcept { return ";
printer.printBaseName(typeDecl);
os << "(ptr); }\n";
os << "};\n";
});
}
void ClangClassTypePrinter::printClassTypeReturnScaffold(
raw_ostream &os, const ClassDecl *type, const ModuleDecl *moduleContext,
llvm::function_ref<void(void)> bodyPrinter) {
os << " return ";
ClangSyntaxPrinter(os).printModuleNamespaceQualifiersIfNeeded(
type->getModuleContext(), moduleContext);
os << cxx_synthesis::getCxxImplNamespaceName() << "::";
ClangValueTypePrinter::printCxxImplClassName(os, type);
os << "::fromUnretained(";
bodyPrinter();
os << ");\n";
}

View File

@@ -0,0 +1,46 @@
//===--- PrintClangClassType.h - Print class types in C/C++ -----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_PRINTASCLANG_PRINTCLANGCLASSTYPE_H
#define SWIFT_PRINTASCLANG_PRINTCLANGCLASSTYPE_H
#include "swift/Basic/LLVM.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/raw_ostream.h"
namespace swift {
class ClassDecl;
class ModuleDecl;
/// Responsible for printing a Swift class decl or in C or C++ mode, to
/// be included in a Swift module's generated clang header.
class ClangClassTypePrinter {
public:
ClangClassTypePrinter(raw_ostream &os) : os(os) {}
/// Print the C++ class definition that corresponds to the given Swift class.
void printClassTypeDecl(const ClassDecl *typeDecl,
llvm::function_ref<void(void)> bodyPrinter);
static void
printClassTypeReturnScaffold(raw_ostream &os, const ClassDecl *type,
const ModuleDecl *moduleContext,
llvm::function_ref<void(void)> bodyPrinter);
private:
raw_ostream &os;
};
} // end namespace swift
#endif

View File

@@ -15,6 +15,7 @@
#include "DeclAndTypePrinter.h"
#include "OutputLanguageMode.h"
#include "PrimitiveTypeMapping.h"
#include "PrintClangClassType.h"
#include "PrintClangValueType.h"
#include "SwiftToClangInteropContext.h"
#include "swift/AST/Decl.h"
@@ -147,6 +148,21 @@ public:
visitPart(aliasTy->getSinglyDesugaredType(), optionalKind, isInOutParam);
}
void visitClassType(ClassType *CT, Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
// FIXME: handle optionalKind.
// FIXME: handle isInOutParam.
if (languageMode != OutputLanguageMode::Cxx) {
os << "void * _Nonnull";
return;
}
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
os << "const ";
ClangSyntaxPrinter(os).printBaseName(CT->getDecl());
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
os << "&";
}
void visitEnumType(EnumType *ET, Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
visitValueType(ET, optionalKind, isInOutParam);
@@ -559,6 +575,12 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
os << ";\n return returnValue;\n";
return;
}
if (auto *classDecl = resultTy->getClassOrBoundGenericClass()) {
ClangClassTypePrinter::printClassTypeReturnScaffold(
os, classDecl, moduleContext,
[&]() { printCallToCFunc(/*additionalParam=*/None); });
return;
}
if (auto *decl = resultTy->getNominalOrBoundGenericNominal()) {
if ((isa<StructDecl>(decl) || isa<EnumDecl>(decl))) {
bool isIndirect =

View File

@@ -28,6 +28,10 @@
namespace swift {
namespace _impl {
extern "C" void *_Nonnull swift_retain(void *_Nonnull) noexcept;
extern "C" void swift_release(void *_Nonnull) noexcept;
inline void *_Nonnull opaqueAlloc(size_t size, size_t align) noexcept {
#if defined(_WIN32)
void *r = _aligned_malloc(size, align);

View File

@@ -0,0 +1,24 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %S/swift-class-in-cxx.swift -typecheck -module-name Class -clang-header-expose-public-decls -emit-clang-header-path %t/class.h
// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-class-execution.o
// RUN: %target-interop-build-swift %S/swift-class-in-cxx.swift -o %t/swift-class-execution -Xlinker %t/swift-class-execution.o -module-name Class -Xfrontend -entry-point-function-name -Xfrontend swiftMain
// RUN: %target-codesign %t/swift-class-execution
// RUN: %target-run %t/swift-class-execution | %FileCheck %s
// REQUIRES: executable_test
#include <assert.h>
#include "class.h"
int main() {
using namespace Class;
// Ensure that the class is released.
auto x = returnClassWithIntField();
// CHECK: init ClassWithIntField
// CHECK-NEXT: destroy ClassWithIntField
return 0;
}

View File

@@ -0,0 +1,61 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Class -clang-header-expose-public-decls -emit-clang-header-path %t/class.h
// RUN: %FileCheck %s < %t/class.h
// RUN: %check-interop-cxx-header-in-clang(%t/class.h)
public final class ClassWithIntField {
let field: Int64
init() {
field = 0
print("init ClassWithIntField")
}
deinit {
print("destroy ClassWithIntField")
}
}
// CHECK: namespace Class {
// CHECK: SWIFT_EXTERN void * _Nonnull $s5Class06returnA12WithIntFieldAA0acdE0CyF(void) SWIFT_NOEXCEPT SWIFT_CALL; // returnClassWithIntField()
// CHECK: namespace Class {
// CHECK: namespace _impl {
// CHECK-EMPTY:
// CHECK-NEXT: class _impl_ClassWithIntField;
// CHECK-EMPTY:
// CHECK-NEXT: } // namespace _impl
// CHECK-EMPTY:
// CHECK-NEXT: class ClassWithIntField final {
// CHECK-NEXT: public:
// CHECK-NEXT: inline ~ClassWithIntField() { swift::_impl::swift_release(_opaquePointer); }
// CHECK-NEXT: inline ClassWithIntField(ClassWithIntField&&) noexcept = default;
// CHECK-NEXT: private:
// CHECK-NEXT: inline ClassWithIntField(void * _Nonnull ptr) noexcept : _opaquePointer(ptr) {}
// CHECK-EMPTY:
// CHECK-NEXT: void * _Nonnull _opaquePointer;
// CHECK-NEXT: friend class _impl::_impl_ClassWithIntField;
// CHECK-NEXT: };
// CHECK-EMPTY:
// CHECK-NEXT:namespace _impl {
// CHECK-EMPTY:
// CHECK-NEXT:class _impl_ClassWithIntField {
// CHECK-NEXT:public:
// CHECK-NEXT:static inline ClassWithIntField fromUnretained(void * _Nonnull ptr) noexcept { return ClassWithIntField(ptr); }
// CHECK-NEXT:};
// CHECK-EMPTY:
// CHECK-NEXT:} // namespace _impl
public final class register { }
// CHECK: class register_ final {
public func returnClassWithIntField() -> ClassWithIntField {
return ClassWithIntField()
}
// CHECK: inline ClassWithIntField returnClassWithIntField() noexcept SWIFT_WARN_UNUSED_RESULT {
// CHECK-NEXT: return _impl::_impl_ClassWithIntField::fromUnretained(_impl::$s5Class06returnA12WithIntFieldAA0acdE0CyF());
// CHECK-NEXT: }