mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -5,6 +5,7 @@ add_swift_host_library(swiftPrintAsClang STATIC
|
||||
ModuleContentsWriter.cpp
|
||||
PrimitiveTypeMapping.cpp
|
||||
PrintAsClang.cpp
|
||||
PrintClangClassType.cpp
|
||||
PrintClangFunction.cpp
|
||||
PrintClangValueType.cpp
|
||||
PrintSwiftToClangCoreScaffold.cpp
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
91
lib/PrintAsClang/PrintClangClassType.cpp
Normal file
91
lib/PrintAsClang/PrintClangClassType.cpp
Normal 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";
|
||||
}
|
||||
46
lib/PrintAsClang/PrintClangClassType.h
Normal file
46
lib/PrintAsClang/PrintClangClassType.h
Normal 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
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
|
||||
24
test/Interop/SwiftToCxx/class/swift-class-execution.cpp
Normal file
24
test/Interop/SwiftToCxx/class/swift-class-execution.cpp
Normal 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;
|
||||
}
|
||||
61
test/Interop/SwiftToCxx/class/swift-class-in-cxx.swift
Normal file
61
test/Interop/SwiftToCxx/class/swift-class-in-cxx.swift
Normal 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: }
|
||||
Reference in New Issue
Block a user