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
|
ModuleContentsWriter.cpp
|
||||||
PrimitiveTypeMapping.cpp
|
PrimitiveTypeMapping.cpp
|
||||||
PrintAsClang.cpp
|
PrintAsClang.cpp
|
||||||
|
PrintClangClassType.cpp
|
||||||
PrintClangFunction.cpp
|
PrintClangFunction.cpp
|
||||||
PrintClangValueType.cpp
|
PrintClangValueType.cpp
|
||||||
PrintSwiftToClangCoreScaffold.cpp
|
PrintSwiftToClangCoreScaffold.cpp
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "DeclAndTypePrinter.h"
|
#include "DeclAndTypePrinter.h"
|
||||||
#include "ClangSyntaxPrinter.h"
|
#include "ClangSyntaxPrinter.h"
|
||||||
#include "PrimitiveTypeMapping.h"
|
#include "PrimitiveTypeMapping.h"
|
||||||
|
#include "PrintClangClassType.h"
|
||||||
#include "PrintClangFunction.h"
|
#include "PrintClangFunction.h"
|
||||||
#include "PrintClangValueType.h"
|
#include "PrintClangValueType.h"
|
||||||
#include "SwiftToClangInteropContext.h"
|
#include "SwiftToClangInteropContext.h"
|
||||||
@@ -296,6 +297,15 @@ private:
|
|||||||
void visitClassDecl(ClassDecl *CD) {
|
void visitClassDecl(ClassDecl *CD) {
|
||||||
printDocumentationComment(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
|
// 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,
|
// of asking if the class is weak imported. If the class has availability,
|
||||||
// we'll print a SWIFT_AVAILABLE() which implies __attribute__((weak_imported))
|
// we'll print a SWIFT_AVAILABLE() which implies __attribute__((weak_imported))
|
||||||
|
|||||||
@@ -605,6 +605,8 @@ public:
|
|||||||
|
|
||||||
if (auto ED = dyn_cast<EnumDecl>(D)) {
|
if (auto ED = dyn_cast<EnumDecl>(D)) {
|
||||||
success = writeEnum(ED);
|
success = writeEnum(ED);
|
||||||
|
} else if (auto CD = dyn_cast<ClassDecl>(D)) {
|
||||||
|
success = writeClass(CD);
|
||||||
} else if (outputLangMode == OutputLanguageMode::Cxx) {
|
} else if (outputLangMode == OutputLanguageMode::Cxx) {
|
||||||
if (auto FD = dyn_cast<FuncDecl>(D))
|
if (auto FD = dyn_cast<FuncDecl>(D))
|
||||||
success = writeFunc(FD);
|
success = writeFunc(FD);
|
||||||
@@ -612,9 +614,7 @@ public:
|
|||||||
success = writeStruct(SD);
|
success = writeStruct(SD);
|
||||||
// FIXME: Warn on unsupported exported decl.
|
// FIXME: Warn on unsupported exported decl.
|
||||||
} else if (isa<ValueDecl>(D)) {
|
} else if (isa<ValueDecl>(D)) {
|
||||||
if (auto CD = dyn_cast<ClassDecl>(D))
|
if (auto PD = dyn_cast<ProtocolDecl>(D))
|
||||||
success = writeClass(CD);
|
|
||||||
else if (auto PD = dyn_cast<ProtocolDecl>(D))
|
|
||||||
success = writeProtocol(PD);
|
success = writeProtocol(PD);
|
||||||
else if (auto ED = dyn_cast<FuncDecl>(D))
|
else if (auto ED = dyn_cast<FuncDecl>(D))
|
||||||
success = writeFunc(ED);
|
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 "DeclAndTypePrinter.h"
|
||||||
#include "OutputLanguageMode.h"
|
#include "OutputLanguageMode.h"
|
||||||
#include "PrimitiveTypeMapping.h"
|
#include "PrimitiveTypeMapping.h"
|
||||||
|
#include "PrintClangClassType.h"
|
||||||
#include "PrintClangValueType.h"
|
#include "PrintClangValueType.h"
|
||||||
#include "SwiftToClangInteropContext.h"
|
#include "SwiftToClangInteropContext.h"
|
||||||
#include "swift/AST/Decl.h"
|
#include "swift/AST/Decl.h"
|
||||||
@@ -147,6 +148,21 @@ public:
|
|||||||
visitPart(aliasTy->getSinglyDesugaredType(), optionalKind, isInOutParam);
|
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,
|
void visitEnumType(EnumType *ET, Optional<OptionalTypeKind> optionalKind,
|
||||||
bool isInOutParam) {
|
bool isInOutParam) {
|
||||||
visitValueType(ET, optionalKind, isInOutParam);
|
visitValueType(ET, optionalKind, isInOutParam);
|
||||||
@@ -559,6 +575,12 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
|
|||||||
os << ";\n return returnValue;\n";
|
os << ";\n return returnValue;\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (auto *classDecl = resultTy->getClassOrBoundGenericClass()) {
|
||||||
|
ClangClassTypePrinter::printClassTypeReturnScaffold(
|
||||||
|
os, classDecl, moduleContext,
|
||||||
|
[&]() { printCallToCFunc(/*additionalParam=*/None); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (auto *decl = resultTy->getNominalOrBoundGenericNominal()) {
|
if (auto *decl = resultTy->getNominalOrBoundGenericNominal()) {
|
||||||
if ((isa<StructDecl>(decl) || isa<EnumDecl>(decl))) {
|
if ((isa<StructDecl>(decl) || isa<EnumDecl>(decl))) {
|
||||||
bool isIndirect =
|
bool isIndirect =
|
||||||
|
|||||||
@@ -28,6 +28,10 @@
|
|||||||
namespace swift {
|
namespace swift {
|
||||||
namespace _impl {
|
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 {
|
inline void *_Nonnull opaqueAlloc(size_t size, size_t align) noexcept {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
void *r = _aligned_malloc(size, align);
|
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