Skip to content

Commit 26d55a2

Browse files
committed
[interop][SwiftToCxx] start emitting bindings for Swift class types
This includes release on destruction, and correctly returning class values from Swift to C++.
1 parent 700ef38 commit 26d55a2

File tree

9 files changed

+262
-3
lines changed

9 files changed

+262
-3
lines changed

lib/PrintAsClang/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_swift_host_library(swiftPrintAsClang STATIC
55
ModuleContentsWriter.cpp
66
PrimitiveTypeMapping.cpp
77
PrintAsClang.cpp
8+
PrintClangClassType.cpp
89
PrintClangFunction.cpp
910
PrintClangValueType.cpp
1011
PrintSwiftToClangCoreScaffold.cpp

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "DeclAndTypePrinter.h"
1414
#include "ClangSyntaxPrinter.h"
1515
#include "PrimitiveTypeMapping.h"
16+
#include "PrintClangClassType.h"
1617
#include "PrintClangFunction.h"
1718
#include "PrintClangValueType.h"
1819
#include "SwiftToClangInteropContext.h"
@@ -296,6 +297,15 @@ class DeclAndTypePrinter::Implementation
296297
void visitClassDecl(ClassDecl *CD) {
297298
printDocumentationComment(CD);
298299

300+
if (outputLang == OutputLanguageMode::Cxx) {
301+
// FIXME: Non objc class.
302+
// FIXME: Print availability.
303+
ClangClassTypePrinter(os).printClassTypeDecl(CD, []() {
304+
// FIXME: print class body.
305+
});
306+
return;
307+
}
308+
299309
// This is just for testing, so we check explicitly for the attribute instead
300310
// of asking if the class is weak imported. If the class has availability,
301311
// we'll print a SWIFT_AVAILABLE() which implies __attribute__((weak_imported))

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -605,16 +605,16 @@ class ModuleWriter {
605605

606606
if (auto ED = dyn_cast<EnumDecl>(D)) {
607607
success = writeEnum(ED);
608+
} else if (auto CD = dyn_cast<ClassDecl>(D)) {
609+
success = writeClass(CD);
608610
} else if (outputLangMode == OutputLanguageMode::Cxx) {
609611
if (auto FD = dyn_cast<FuncDecl>(D))
610612
success = writeFunc(FD);
611613
if (auto SD = dyn_cast<StructDecl>(D))
612614
success = writeStruct(SD);
613615
// FIXME: Warn on unsupported exported decl.
614616
} else if (isa<ValueDecl>(D)) {
615-
if (auto CD = dyn_cast<ClassDecl>(D))
616-
success = writeClass(CD);
617-
else if (auto PD = dyn_cast<ProtocolDecl>(D))
617+
if (auto PD = dyn_cast<ProtocolDecl>(D))
618618
success = writeProtocol(PD);
619619
else if (auto ED = dyn_cast<FuncDecl>(D))
620620
success = writeFunc(ED);
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//===--- PrintClangClassType.cpp - Print class types in C/C++ ---*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "PrintClangClassType.h"
14+
#include "ClangSyntaxPrinter.h"
15+
#include "PrintClangValueType.h"
16+
#include "swift/AST/Decl.h"
17+
18+
using namespace swift;
19+
20+
void ClangClassTypePrinter::printClassTypeDecl(
21+
const ClassDecl *typeDecl, llvm::function_ref<void(void)> bodyPrinter) {
22+
auto printCxxImplClassName = ClangValueTypePrinter::printCxxImplClassName;
23+
24+
ClangSyntaxPrinter printer(os);
25+
26+
// Print out a forward declaration of the "hidden" _impl class.
27+
printer.printNamespace(cxx_synthesis::getCxxImplNamespaceName(),
28+
[&](raw_ostream &os) {
29+
os << "class ";
30+
printCxxImplClassName(os, typeDecl);
31+
os << ";\n";
32+
});
33+
34+
os << "class ";
35+
printer.printBaseName(typeDecl);
36+
// FIXME: Add support for inherintance.
37+
os << " final";
38+
os << " {\n";
39+
os << "public:\n";
40+
41+
// Destructor releases the object.
42+
os << " inline ~";
43+
printer.printBaseName(typeDecl);
44+
os << "() { swift::" << cxx_synthesis::getCxxImplNamespaceName()
45+
<< "::swift_release(_opaquePointer); }\n";
46+
47+
// FIXME: move semantics should be restricted?
48+
os << " inline ";
49+
printer.printBaseName(typeDecl);
50+
os << "(";
51+
printer.printBaseName(typeDecl);
52+
os << "&&) noexcept = default;\n";
53+
54+
os << "private:\n";
55+
os << " inline ";
56+
printer.printBaseName(typeDecl);
57+
os << "(void * _Nonnull ptr) noexcept : _opaquePointer(ptr) {}\n";
58+
os << "\n void * _Nonnull _opaquePointer;\n";
59+
os << " friend class " << cxx_synthesis::getCxxImplNamespaceName() << "::";
60+
printCxxImplClassName(os, typeDecl);
61+
os << ";\n";
62+
os << "};\n\n";
63+
64+
// Print out the "hidden" _impl class.
65+
printer.printNamespace(
66+
cxx_synthesis::getCxxImplNamespaceName(), [&](raw_ostream &os) {
67+
os << "class ";
68+
printCxxImplClassName(os, typeDecl);
69+
os << " {\n";
70+
os << "public:\n";
71+
os << "static inline ";
72+
printer.printBaseName(typeDecl);
73+
os << " fromUnretained(void * _Nonnull ptr) noexcept { return ";
74+
printer.printBaseName(typeDecl);
75+
os << "(ptr); }\n";
76+
os << "};\n";
77+
});
78+
}
79+
80+
void ClangClassTypePrinter::printClassTypeReturnScaffold(
81+
raw_ostream &os, const ClassDecl *type, const ModuleDecl *moduleContext,
82+
llvm::function_ref<void(void)> bodyPrinter) {
83+
os << " return ";
84+
ClangSyntaxPrinter(os).printModuleNamespaceQualifiersIfNeeded(
85+
type->getModuleContext(), moduleContext);
86+
os << cxx_synthesis::getCxxImplNamespaceName() << "::";
87+
ClangValueTypePrinter::printCxxImplClassName(os, type);
88+
os << "::fromUnretained(";
89+
bodyPrinter();
90+
os << ");\n";
91+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===--- PrintClangClassType.h - Print class types in C/C++ -----*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_PRINTASCLANG_PRINTCLANGCLASSTYPE_H
14+
#define SWIFT_PRINTASCLANG_PRINTCLANGCLASSTYPE_H
15+
16+
#include "swift/Basic/LLVM.h"
17+
#include "llvm/ADT/STLExtras.h"
18+
#include "llvm/Support/raw_ostream.h"
19+
20+
namespace swift {
21+
22+
class ClassDecl;
23+
class ModuleDecl;
24+
25+
/// Responsible for printing a Swift class decl or in C or C++ mode, to
26+
/// be included in a Swift module's generated clang header.
27+
class ClangClassTypePrinter {
28+
public:
29+
ClangClassTypePrinter(raw_ostream &os) : os(os) {}
30+
31+
/// Print the C++ class definition that corresponds to the given Swift class.
32+
void printClassTypeDecl(const ClassDecl *typeDecl,
33+
llvm::function_ref<void(void)> bodyPrinter);
34+
35+
static void
36+
printClassTypeReturnScaffold(raw_ostream &os, const ClassDecl *type,
37+
const ModuleDecl *moduleContext,
38+
llvm::function_ref<void(void)> bodyPrinter);
39+
40+
private:
41+
raw_ostream &os;
42+
};
43+
44+
} // end namespace swift
45+
46+
#endif

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "DeclAndTypePrinter.h"
1616
#include "OutputLanguageMode.h"
1717
#include "PrimitiveTypeMapping.h"
18+
#include "PrintClangClassType.h"
1819
#include "PrintClangValueType.h"
1920
#include "SwiftToClangInteropContext.h"
2021
#include "swift/AST/Decl.h"
@@ -147,6 +148,21 @@ class CFunctionSignatureTypePrinter
147148
visitPart(aliasTy->getSinglyDesugaredType(), optionalKind, isInOutParam);
148149
}
149150

151+
void visitClassType(ClassType *CT, Optional<OptionalTypeKind> optionalKind,
152+
bool isInOutParam) {
153+
// FIXME: handle optionalKind.
154+
// FIXME: handle isInOutParam.
155+
if (languageMode != OutputLanguageMode::Cxx) {
156+
os << "void * _Nonnull";
157+
return;
158+
}
159+
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
160+
os << "const ";
161+
ClangSyntaxPrinter(os).printBaseName(CT->getDecl());
162+
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
163+
os << "&";
164+
}
165+
150166
void visitEnumType(EnumType *ET, Optional<OptionalTypeKind> optionalKind,
151167
bool isInOutParam) {
152168
visitValueType(ET, optionalKind, isInOutParam);
@@ -559,6 +575,12 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
559575
os << ";\n return returnValue;\n";
560576
return;
561577
}
578+
if (auto *classDecl = resultTy->getClassOrBoundGenericClass()) {
579+
ClangClassTypePrinter::printClassTypeReturnScaffold(
580+
os, classDecl, moduleContext,
581+
[&]() { printCallToCFunc(/*additionalParam=*/None); });
582+
return;
583+
}
562584
if (auto *decl = resultTy->getNominalOrBoundGenericNominal()) {
563585
if ((isa<StructDecl>(decl) || isa<EnumDecl>(decl))) {
564586
bool isIndirect =

stdlib/public/SwiftShims/_SwiftCxxInteroperability.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
namespace swift {
2929
namespace _impl {
3030

31+
extern "C" void *_Nonnull swift_retain(void *_Nonnull) noexcept;
32+
33+
extern "C" void swift_release(void *_Nonnull) noexcept;
34+
3135
inline void *_Nonnull opaqueAlloc(size_t size, size_t align) noexcept {
3236
#if defined(_WIN32)
3337
void *r = _aligned_malloc(size, align);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// 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
4+
5+
// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-class-execution.o
6+
// 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
7+
8+
// RUN: %target-codesign %t/swift-class-execution
9+
// RUN: %target-run %t/swift-class-execution | %FileCheck %s
10+
11+
// REQUIRES: executable_test
12+
13+
#include <assert.h>
14+
#include "class.h"
15+
16+
int main() {
17+
using namespace Class;
18+
19+
// Ensure that the class is released.
20+
auto x = returnClassWithIntField();
21+
// CHECK: init ClassWithIntField
22+
// CHECK-NEXT: destroy ClassWithIntField
23+
return 0;
24+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -typecheck -module-name Class -clang-header-expose-public-decls -emit-clang-header-path %t/class.h
3+
// RUN: %FileCheck %s < %t/class.h
4+
5+
// RUN: %check-interop-cxx-header-in-clang(%t/class.h)
6+
7+
public final class ClassWithIntField {
8+
let field: Int64
9+
10+
init() {
11+
field = 0
12+
print("init ClassWithIntField")
13+
}
14+
deinit {
15+
print("destroy ClassWithIntField")
16+
}
17+
}
18+
19+
// CHECK: namespace Class {
20+
21+
// CHECK: SWIFT_EXTERN void * _Nonnull $s5Class06returnA12WithIntFieldAA0acdE0CyF(void) SWIFT_NOEXCEPT SWIFT_CALL; // returnClassWithIntField()
22+
23+
// CHECK: namespace Class {
24+
25+
// CHECK: namespace _impl {
26+
// CHECK-EMPTY:
27+
// CHECK-NEXT: class _impl_ClassWithIntField;
28+
// CHECK-EMPTY:
29+
// CHECK-NEXT: } // namespace _impl
30+
// CHECK-EMPTY:
31+
// CHECK-NEXT: class ClassWithIntField final {
32+
// CHECK-NEXT: public:
33+
// CHECK-NEXT: inline ~ClassWithIntField() { swift::_impl::swift_release(_opaquePointer); }
34+
// CHECK-NEXT: inline ClassWithIntField(ClassWithIntField&&) noexcept = default;
35+
// CHECK-NEXT: private:
36+
// CHECK-NEXT: inline ClassWithIntField(void * _Nonnull ptr) noexcept : _opaquePointer(ptr) {}
37+
// CHECK-EMPTY:
38+
// CHECK-NEXT: void * _Nonnull _opaquePointer;
39+
// CHECK-NEXT: friend class _impl::_impl_ClassWithIntField;
40+
// CHECK-NEXT: };
41+
// CHECK-EMPTY:
42+
// CHECK-NEXT:namespace _impl {
43+
// CHECK-EMPTY:
44+
// CHECK-NEXT:class _impl_ClassWithIntField {
45+
// CHECK-NEXT:public:
46+
// CHECK-NEXT:static inline ClassWithIntField fromUnretained(void * _Nonnull ptr) noexcept { return ClassWithIntField(ptr); }
47+
// CHECK-NEXT:};
48+
// CHECK-EMPTY:
49+
// CHECK-NEXT:} // namespace _impl
50+
51+
public final class register { }
52+
53+
// CHECK: class register_ final {
54+
55+
public func returnClassWithIntField() -> ClassWithIntField {
56+
return ClassWithIntField()
57+
}
58+
59+
// CHECK: inline ClassWithIntField returnClassWithIntField() noexcept SWIFT_WARN_UNUSED_RESULT {
60+
// CHECK-NEXT: return _impl::_impl_ClassWithIntField::fromUnretained(_impl::$s5Class06returnA12WithIntFieldAA0acdE0CyF());
61+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)