Skip to content

Commit 4d0d4af

Browse files
authored
Merge pull request #59419 from apple/eng/copy-destroy-swift-value-in-cxx
[interop][SwiftToCxx] destroy and initWithCopy Swift structs in C++
2 parents 7ba07ae + f9aa02b commit 4d0d4af

10 files changed

+255
-7
lines changed

lib/PrintAsClang/ClangSyntaxPrinter.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,15 @@ void ClangSyntaxPrinter::printNullability(
127127
if (printKind != NullabilityPrintKind::After)
128128
os << ' ';
129129
}
130+
131+
void ClangSyntaxPrinter::printSwiftTypeMetadataAccessFunctionCall(
132+
StringRef name) {
133+
os << name << "(0)";
134+
}
135+
136+
void ClangSyntaxPrinter::printValueWitnessTableAccessFromTypeMetadata(
137+
StringRef metadataVariable) {
138+
os << "*(reinterpret_cast<";
139+
printSwiftImplQualifier();
140+
os << "ValueWitnessTable **>(" << metadataVariable << "._0) - 1)";
141+
}

lib/PrintAsClang/ClangSyntaxPrinter.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ class ClangSyntaxPrinter {
7777
static bool isClangKeyword(StringRef name);
7878
static bool isClangKeyword(Identifier name);
7979

80+
/// Print the call expression to the Swift type metadata access function.
81+
void printSwiftTypeMetadataAccessFunctionCall(StringRef name);
82+
83+
/// Print the expression to access the value witness table pointer from the
84+
/// given type metadata variable.
85+
void printValueWitnessTableAccessFromTypeMetadata(StringRef metadataVariable);
86+
8087
protected:
8188
raw_ostream &os;
8289
};

lib/PrintAsClang/PrintAsClang.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ static void emitObjCConditional(raw_ostream &out,
5151
out << "#endif\n";
5252
}
5353

54+
static void writePtrauthPrologue(raw_ostream &os) {
55+
emitCxxConditional(os, [&]() {
56+
os << "#if __has_include(<ptrauth.h>)\n";
57+
os << "# include <ptrauth.h>\n";
58+
os << "#else\n";
59+
os << "# ifndef __ptrauth_swift_value_witness_function_pointer\n";
60+
os << "# define __ptrauth_swift_value_witness_function_pointer(x)\n";
61+
os << "# endif\n";
62+
os << "#endif\n";
63+
});
64+
}
65+
5466
static void writePrologue(raw_ostream &out, ASTContext &ctx,
5567
StringRef macroGuard) {
5668

@@ -98,6 +110,7 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx,
98110
"#include <stdbool.h>\n"
99111
"#include <string.h>\n";
100112
});
113+
writePtrauthPrologue(out);
101114
out << "\n"
102115
"#if !defined(SWIFT_TYPEDEFS)\n"
103116
"# define SWIFT_TYPEDEFS 1\n"

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,13 @@ printCValueTypeStorageStruct(raw_ostream &os, const NominalTypeDecl *typeDecl,
6161
}
6262

6363
void printCTypeMetadataTypeFunction(raw_ostream &os,
64-
const NominalTypeDecl *typeDecl) {
64+
const NominalTypeDecl *typeDecl,
65+
StringRef typeMetadataFuncName) {
6566
os << "// Type metadata accessor for " << typeDecl->getNameStr() << "\n";
66-
auto entity = irgen::LinkEntity::forTypeMetadataAccessFunction(
67-
typeDecl->getDeclaredType()->getCanonicalType());
6867
os << "SWIFT_EXTERN ";
6968
ClangSyntaxPrinter printer(os);
7069
printer.printSwiftImplQualifier();
71-
os << "MetadataResponseTy ";
72-
entity.mangle(os);
73-
os << '(';
70+
os << "MetadataResponseTy " << typeMetadataFuncName << '(';
7471
printer.printSwiftImplQualifier();
7572
os << "MetadataRequestTy)";
7673
os << " SWIFT_NOEXCEPT SWIFT_CALL;\n\n";
@@ -90,6 +87,10 @@ void ClangValueTypePrinter::printStructDecl(const StructDecl *SD) {
9087

9188
ClangSyntaxPrinter printer(os);
9289

90+
auto typeMetadataFunc = irgen::LinkEntity::forTypeMetadataAccessFunction(
91+
SD->getDeclaredType()->getCanonicalType());
92+
std::string typeMetadataFuncName = typeMetadataFunc.mangleAsString();
93+
9394
// Print out a forward declaration of the "hidden" _impl class.
9495
printer.printNamespace(cxx_synthesis::getCxxImplNamespaceName(),
9596
[&](raw_ostream &os) {
@@ -99,13 +100,53 @@ void ClangValueTypePrinter::printStructDecl(const StructDecl *SD) {
99100

100101
// Print out special functions, like functions that
101102
// access type metadata.
102-
printCTypeMetadataTypeFunction(os, SD);
103+
printCTypeMetadataTypeFunction(os, SD,
104+
typeMetadataFuncName);
103105
});
104106

105107
// Print out the C++ class itself.
106108
os << "class ";
107109
ClangSyntaxPrinter(os).printBaseName(SD);
108110
os << " final {\n";
111+
os << "public:\n";
112+
113+
// Print out the destructor.
114+
os << " inline ~";
115+
printer.printBaseName(SD);
116+
os << "() {\n";
117+
os << " auto metadata = " << cxx_synthesis::getCxxImplNamespaceName()
118+
<< "::";
119+
printer.printSwiftTypeMetadataAccessFunctionCall(typeMetadataFuncName);
120+
os << ";\n";
121+
os << " auto *vwTable = ";
122+
printer.printValueWitnessTableAccessFromTypeMetadata("metadata");
123+
os << ";\n";
124+
os << " vwTable->destroy(_getOpaquePointer(), metadata._0);\n";
125+
os << " }\n";
126+
127+
os << " inline ";
128+
printer.printBaseName(SD);
129+
os << "(const ";
130+
printer.printBaseName(SD);
131+
os << " &other) {\n";
132+
os << " auto metadata = " << cxx_synthesis::getCxxImplNamespaceName()
133+
<< "::";
134+
printer.printSwiftTypeMetadataAccessFunctionCall(typeMetadataFuncName);
135+
os << ";\n";
136+
os << " auto *vwTable = ";
137+
printer.printValueWitnessTableAccessFromTypeMetadata("metadata");
138+
os << ";\n";
139+
os << " vwTable->initializeWithCopy(_getOpaquePointer(), const_cast<char "
140+
"*>(other._getOpaquePointer()), metadata._0);\n";
141+
os << " }\n";
142+
143+
// FIXME: the move constructor should be hidden somehow.
144+
os << " inline ";
145+
printer.printBaseName(SD);
146+
os << "(";
147+
printer.printBaseName(SD);
148+
os << " &&) = default;\n";
149+
109150
// FIXME: Print the other members of the struct.
110151
os << "private:\n";
111152

lib/PrintAsClang/PrintSwiftToClangCoreScaffold.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
#include "ClangSyntaxPrinter.h"
1515
#include "PrimitiveTypeMapping.h"
1616
#include "SwiftToClangInteropContext.h"
17+
#include "swift/ABI/MetadataValues.h"
1718
#include "swift/AST/Decl.h"
1819
#include "swift/AST/Type.h"
1920
#include "swift/IRGen/IRABIDetailsProvider.h"
21+
#include "llvm/ADT/STLExtras.h"
2022

2123
using namespace swift;
2224

@@ -60,6 +62,49 @@ static void printKnownType(
6062
printKnownStruct(typeMapping, os, name, typeRecord);
6163
}
6264

65+
static void printValueWitnessTableFunctionType(raw_ostream &os, StringRef name,
66+
StringRef returnType,
67+
std::string paramTypes,
68+
uint16_t ptrauthDisc) {
69+
os << "using ValueWitness" << name << "Ty = " << returnType << "(*)("
70+
<< paramTypes << ") __ptrauth_swift_value_witness_function_pointer("
71+
<< ptrauthDisc << ");\n";
72+
}
73+
74+
static std::string makeParams(const char *arg) { return arg; }
75+
76+
template <class... T>
77+
static std::string makeParams(const char *arg, const T... args) {
78+
return std::string(arg) + ", " + makeParams(args...);
79+
}
80+
81+
static void printValueWitnessTable(raw_ostream &os) {
82+
std::string members;
83+
llvm::raw_string_ostream membersOS(members);
84+
85+
#define WANT_ONLY_REQUIRED_VALUE_WITNESSES
86+
#define DATA_VALUE_WITNESS(lowerId, upperId, type) \
87+
membersOS << " " << type << " " << #lowerId << ";\n";
88+
#define FUNCTION_VALUE_WITNESS(lowerId, upperId, returnType, paramTypes) \
89+
printValueWitnessTableFunctionType( \
90+
os, #upperId, returnType, makeParams paramTypes, \
91+
SpecialPointerAuthDiscriminators::upperId); \
92+
membersOS << " ValueWitness" << #upperId << "Ty _Nonnull " << #lowerId \
93+
<< ";\n";
94+
#define MUTABLE_VALUE_TYPE "void * _Nonnull"
95+
#define IMMUTABLE_VALUE_TYPE "const void * _Nonnull"
96+
#define MUTABLE_BUFFER_TYPE "void * _Nonnull"
97+
#define IMMUTABLE_BUFFER_TYPE "const void * _Nonnull"
98+
#define TYPE_TYPE "void * _Nonnull"
99+
#define SIZE_TYPE "size_t"
100+
#define INT_TYPE "int"
101+
#define UINT_TYPE "unsigned"
102+
#define VOID_TYPE "void"
103+
#include "swift/ABI/ValueWitness.def"
104+
105+
os << "\nstruct ValueWitnessTable {\n" << membersOS.str() << "};\n";
106+
}
107+
63108
static void printTypeMetadataResponseType(SwiftToClangInteropContext &ctx,
64109
PrimitiveTypeMapping &typeMapping,
65110
raw_ostream &os) {
@@ -82,6 +127,8 @@ void swift::printSwiftToClangCoreScaffold(SwiftToClangInteropContext &ctx,
82127
cxx_synthesis::getCxxImplNamespaceName(), [&](raw_ostream &) {
83128
printer.printExternC([&](raw_ostream &os) {
84129
printTypeMetadataResponseType(ctx, typeMapping, os);
130+
os << "\n";
131+
printValueWitnessTable(os);
85132
});
86133
});
87134
});

test/Interop/SwiftToCxx/core/swift-impl-defs-in-cxx.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,30 @@
2323
// CHECK-NEXT: // Swift type metadata request type.
2424
// CHECK-NEXT: typedef uint{{.*}}_t MetadataRequestTy;
2525
// CHECK-EMPTY:
26+
// CHECK-NEXT: using ValueWitnessInitializeBufferWithCopyOfBufferTy = void * _Nonnull(*)(void * _Nonnull, void * _Nonnull, void * _Nonnull) __ptrauth_swift_value_witness_function_pointer(
27+
// CHECK-NEXT: using ValueWitnessDestroyTy = void(*)(void * _Nonnull, void * _Nonnull) __ptrauth_swift_value_witness_function_pointer(
28+
// CHECK-NEXT: using ValueWitnessInitializeWithCopyTy = void * _Nonnull(*)(void * _Nonnull, void * _Nonnull, void * _Nonnull) __ptrauth_swift_value_witness_function_pointer(
29+
// CHECK-NEXT: using ValueWitnessAssignWithCopyTy = void * _Nonnull(*)(void * _Nonnull, void * _Nonnull, void * _Nonnull) __ptrauth_swift_value_witness_function_pointer(
30+
// CHECK-NEXT: using ValueWitnessInitializeWithTakeTy = void * _Nonnull(*)(void * _Nonnull, void * _Nonnull, void * _Nonnull) __ptrauth_swift_value_witness_function_pointer(
31+
// CHECK-NEXT: using ValueWitnessAssignWithTakeTy = void * _Nonnull(*)(void * _Nonnull, void * _Nonnull, void * _Nonnull) __ptrauth_swift_value_witness_function_pointer(
32+
// CHECK-NEXT: using ValueWitnessGetEnumTagSinglePayloadTy = unsigned(*)(const void * _Nonnull, unsigned, void * _Nonnull) __ptrauth_swift_value_witness_function_pointer(
33+
// CHECK-NEXT: using ValueWitnessStoreEnumTagSinglePayloadTy = void(*)(void * _Nonnull, unsigned, unsigned, void * _Nonnull) __ptrauth_swift_value_witness_function_pointer(
34+
// CHECK-EMPTY:
35+
// CHECK-NEXT: struct ValueWitnessTable {
36+
// CHECK-NEXT: ValueWitnessInitializeBufferWithCopyOfBufferTy _Nonnull initializeBufferWithCopyOfBuffer;
37+
// CHECK-NEXT: ValueWitnessDestroyTy _Nonnull destroy;
38+
// CHECK-NEXT: ValueWitnessInitializeWithCopyTy _Nonnull initializeWithCopy;
39+
// CHECK-NEXT: ValueWitnessAssignWithCopyTy _Nonnull assignWithCopy;
40+
// CHECK-NEXT: ValueWitnessInitializeWithTakeTy _Nonnull initializeWithTake;
41+
// CHECK-NEXT: ValueWitnessAssignWithTakeTy _Nonnull assignWithTake;
42+
// CHECK-NEXT: ValueWitnessGetEnumTagSinglePayloadTy _Nonnull getEnumTagSinglePayload;
43+
// CHECK-NEXT: ValueWitnessStoreEnumTagSinglePayloadTy _Nonnull storeEnumTagSinglePayload;
44+
// CHECK-NEXT: size_t size;
45+
// CHECK-NEXT: size_t stride;
46+
// CHECK-NEXT: unsigned flags;
47+
// CHECK-NEXT: unsigned extraInhabitantCount;
48+
// CHECK-NEXT: };
49+
// CHECK-EMPTY:
2650
// CHECK-NEXT: #ifdef __cplusplus
2751
// CHECK-NEXT: }
2852
// CHECK-NEXT: #endif
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend %S/struct-with-refcounted-member.swift -typecheck -module-name Structs -clang-header-expose-public-decls -emit-clang-header-path %t/structs.h
4+
5+
// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-structs-execution.o
6+
// RUN: %target-interop-build-swift %S/struct-with-refcounted-member.swift -o %t/swift-structs-execution -Xlinker %t/swift-structs-execution.o -module-name Structs -Xfrontend -entry-point-function-name -Xfrontend swiftMain
7+
8+
// RUN: %target-codesign %t/swift-structs-execution
9+
// RUN: %target-run %t/swift-structs-execution | %FileCheck %s
10+
11+
// REQUIRES: executable_test
12+
13+
#include <assert.h>
14+
#include "structs.h"
15+
16+
int main() {
17+
using namespace Structs;
18+
19+
// Ensure that the value destructor is called.
20+
{
21+
StructWithRefcountedMember value = returnNewStructWithRefcountedMember();
22+
}
23+
printBreak(1);
24+
// CHECK: create RefCountedClass
25+
// CHECK-NEXT: destroy RefCountedClass
26+
// CHECK-NEXT: breakpoint 1
27+
28+
{
29+
StructWithRefcountedMember value = returnNewStructWithRefcountedMember();
30+
StructWithRefcountedMember copyValue(value);
31+
}
32+
printBreak(2);
33+
// CHECK-NEXT: create RefCountedClass
34+
// CHECK-NEXT: destroy RefCountedClass
35+
// CHECK-NEXT: breakpoint 2
36+
37+
{
38+
StructWithRefcountedMember value = returnNewStructWithRefcountedMember();
39+
StructWithRefcountedMember value2(returnNewStructWithRefcountedMember());
40+
}
41+
printBreak(3);
42+
// CHECK-NEXT: create RefCountedClass
43+
// CHECK-NEXT: create RefCountedClass
44+
// CHECK-NEXT: destroy RefCountedClass
45+
// CHECK-NEXT: destroy RefCountedClass
46+
// CHECK-NEXT: breakpoint 3
47+
return 0;
48+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -typecheck -module-name Structs -clang-header-expose-public-decls -emit-clang-header-path %t/structs.h
3+
// RUN: %FileCheck %s < %t/structs.h
4+
5+
// RUN: %check-interop-cxx-header-in-clang(%t/structs.h -Wno-unused-function)
6+
7+
class RefCountedClass {
8+
init() {
9+
print("create RefCountedClass")
10+
}
11+
deinit {
12+
print("destroy RefCountedClass")
13+
}
14+
}
15+
16+
public struct StructWithRefcountedMember {
17+
let x: RefCountedClass
18+
}
19+
20+
public func returnNewStructWithRefcountedMember() -> StructWithRefcountedMember {
21+
return StructWithRefcountedMember(x: RefCountedClass())
22+
}
23+
24+
public func printBreak(_ x: Int) {
25+
print("breakpoint \(x)")
26+
}
27+
28+
// CHECK: class StructWithRefcountedMember final {
29+
// CHECK-NEXT: public:
30+
// CHECK-NEXT: inline ~StructWithRefcountedMember() {
31+
// CHECK-NEXT: auto metadata = _impl::$s7Structs26StructWithRefcountedMemberVMa(0);
32+
// CHECK-NEXT: auto *vwTable = *(reinterpret_cast<swift::_impl::ValueWitnessTable **>(metadata._0) - 1);
33+
// CHECK-NEXT: vwTable->destroy(_getOpaquePointer(), metadata._0);
34+
// CHECK-NEXT: }
35+
// CHECK-NEXT: inline StructWithRefcountedMember(const StructWithRefcountedMember &other) {
36+
// CHECK-NEXT: auto metadata = _impl::$s7Structs26StructWithRefcountedMemberVMa(0);
37+
// CHECK-NEXT: auto *vwTable = *(reinterpret_cast<swift::_impl::ValueWitnessTable **>(metadata._0) - 1);
38+
// CHECK-NEXT: vwTable->initializeWithCopy(_getOpaquePointer(), const_cast<char *>(other._getOpaquePointer()), metadata._0);
39+
// CHECK-NEXT: }
40+
// CHECK-NEXT: inline StructWithRefcountedMember(StructWithRefcountedMember &&) = default;
41+
// CHECK-NEXT: private:

test/Interop/SwiftToCxx/structs/swift-struct-in-cxx.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
// CHECK-NEXT: }
2121

2222
// CHECK: class StructWithIntField final {
23+
// CHECK-NEXT: public:
24+
// CHECK-NEXT: inline ~StructWithIntField() {
25+
// CHECK: }
26+
// CHECK-NEXT: inline StructWithIntField(const StructWithIntField &other) {
27+
// CHECK: }
28+
// CHECK-NEXT: inline StructWithIntField(StructWithIntField &&) = default;
2329
// CHECK-NEXT: private:
2430
// CHECK-NEXT: inline StructWithIntField() {}
2531
// CHECK-NEXT: static inline StructWithIntField _make() { return StructWithIntField(); }

test/PrintAsCxx/empty.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@
3535
// CHECK-NEXT: #include <stdbool.h>
3636
// CHECK-NEXT: #include <string.h>
3737
// CHECK-NEXT: #endif
38+
// CHECK-NEXT: #if defined(__cplusplus)
39+
// CHECK-NEXT: #if __has_include(<ptrauth.h>)
40+
// CHECK-NEXT: # include <ptrauth.h>
41+
// CHECK-NEXT: #else
42+
// CHECK-NEXT: # ifndef __ptrauth_swift_value_witness_function_pointer
43+
// CHECK-NEXT: # define __ptrauth_swift_value_witness_function_pointer(x)
44+
// CHECK-NEXT: # endif
45+
// CHECK-NEXT: #endif
46+
// CHECK-NEXT: #endif
3847

3948
// CHECK-LABEL: !defined(SWIFT_TYPEDEFS)
4049
// CHECK-NEXT: # define SWIFT_TYPEDEFS 1

0 commit comments

Comments
 (0)