Skip to content

Commit 5474d7e

Browse files
committed
[interop][SwiftToCxx] Add support for emitting resilient struct bindings
1 parent f2b4a43 commit 5474d7e

8 files changed

+308
-21
lines changed

lib/PrintAsClang/ClangSyntaxPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ using namespace cxx_synthesis;
1818

1919
StringRef cxx_synthesis::getCxxImplNamespaceName() { return "_impl"; }
2020

21+
StringRef cxx_synthesis::getCxxOpaqueStorageClassName() {
22+
return "OpaqueStorage";
23+
}
24+
2125
bool ClangSyntaxPrinter::isClangKeyword(StringRef name) {
2226
static const llvm::DenseSet<StringRef> keywords = [] {
2327
llvm::DenseSet<StringRef> set;

lib/PrintAsClang/ClangSyntaxPrinter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ namespace cxx_synthesis {
2929
/// module in C++.
3030
StringRef getCxxImplNamespaceName();
3131

32+
/// Return the name of the C++ class inside of `swift::_impl`
33+
/// namespace that holds an opaque value, like a resilient struct.
34+
StringRef getCxxOpaqueStorageClassName();
35+
3236
} // end namespace cxx_synthesis
3337

3438
class ClangSyntaxPrinter {

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ bool isKnownType(Type t, PrimitiveTypeMapping &typeMapping,
5151
return getKnownTypeInfo(typeDecl, typeMapping, languageMode) != None;
5252
}
5353

54+
bool isResilientType(Type t) {
55+
if (auto *typeAliasType = dyn_cast<TypeAliasType>(t.getPointer()))
56+
return isResilientType(typeAliasType->getSinglyDesugaredType());
57+
else if (auto *nominalType = t->getNominalOrBoundGenericNominal())
58+
return nominalType->isResilient();
59+
return false;
60+
}
61+
5462
bool isKnownCxxType(Type t, PrimitiveTypeMapping &typeMapping) {
5563
return isKnownType(t, typeMapping, OutputLanguageMode::Cxx);
5664
}
@@ -135,7 +143,8 @@ class CFunctionSignatureTypePrinter
135143
// FIXME: Handle optional structures.
136144
if (typeUseKind == FunctionSignatureTypeUse::ParamType) {
137145
if (languageMode != OutputLanguageMode::Cxx &&
138-
interopContext.getIrABIDetails().shouldPassIndirectly(ST)) {
146+
(SD->isResilient() ||
147+
interopContext.getIrABIDetails().shouldPassIndirectly(ST))) {
139148
if (modifiersDelegate.prefixIndirectParamValueTypeInC)
140149
(*modifiersDelegate.prefixIndirectParamValueTypeInC)(os);
141150
// FIXME: it would be nice to print out the C struct type here.
@@ -198,7 +207,8 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
198207
bool isIndirectReturnType =
199208
kind == FunctionSignatureKind::CFunctionProto &&
200209
!isKnownCType(resultTy, typeMapping) &&
201-
interopContext.getIrABIDetails().shouldReturnIndirectly(resultTy);
210+
(isResilientType(resultTy) ||
211+
interopContext.getIrABIDetails().shouldReturnIndirectly(resultTy));
202212
if (!isIndirectReturnType) {
203213
OptionalTypeKind retKind;
204214
Type objTy;
@@ -285,7 +295,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
285295
if (auto *structDecl = type->getStructOrBoundGenericStruct()) {
286296
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
287297
.printParameterCxxToCUseScaffold(
288-
interopContext.getIrABIDetails().shouldPassIndirectly(type),
298+
structDecl->isResilient() ||
299+
interopContext.getIrABIDetails().shouldPassIndirectly(type),
289300
structDecl, namePrinter, isInOut,
290301
/*isSelf=*/paramRole &&
291302
*paramRole == AdditionalParam::Role::Self);
@@ -352,6 +363,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
352363
if (!isKnownCxxType(resultTy, typeMapping)) {
353364
if (auto *structDecl = resultTy->getStructOrBoundGenericStruct()) {
354365
bool isIndirect =
366+
structDecl->isResilient() ||
355367
interopContext.getIrABIDetails().shouldReturnIndirectly(resultTy);
356368
ClangValueTypePrinter valueTypePrinter(os, cPrologueOS, typeMapping,
357369
interopContext);

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,18 @@ void printCTypeMetadataTypeFunction(raw_ostream &os,
7676
void ClangValueTypePrinter::printValueTypeDecl(
7777
const NominalTypeDecl *typeDecl,
7878
llvm::function_ref<void(void)> bodyPrinter) {
79-
auto typeSizeAlign =
80-
interopContext.getIrABIDetails().getTypeSizeAlignment(typeDecl);
81-
if (!typeSizeAlign) {
82-
// FIXME: handle non-fixed layout structs.
83-
return;
84-
}
85-
if (typeSizeAlign->size == 0) {
86-
// FIXME: How to represent 0 sized structs?
87-
return;
79+
llvm::Optional<IRABIDetailsProvider::SizeAndAlignment> typeSizeAlign;
80+
if (!typeDecl->isResilient()) {
81+
82+
typeSizeAlign =
83+
interopContext.getIrABIDetails().getTypeSizeAlignment(typeDecl);
84+
assert(typeSizeAlign && "unknown layout for non-resilient type!");
85+
if (typeSizeAlign->size == 0) {
86+
// FIXME: How to represent 0 sized structs?
87+
return;
88+
}
8889
}
90+
bool isOpaqueLayout = !typeSizeAlign.hasValue();
8991

9092
ClangSyntaxPrinter printer(os);
9193

@@ -156,23 +158,55 @@ void ClangValueTypePrinter::printValueTypeDecl(
156158
// Print out private default constructor.
157159
os << " inline ";
158160
printer.printBaseName(typeDecl);
159-
os << "() {}\n";
161+
if (isOpaqueLayout) {
162+
os << "(";
163+
printer.printSwiftImplQualifier();
164+
os << "ValueWitnessTable *vwTable) : _storage(vwTable) {}\n";
165+
} else {
166+
os << "() {}\n";
167+
}
160168
// Print out '_make' function which returns an unitialized instance for
161169
// passing to Swift.
162170
os << " static inline ";
163171
printer.printBaseName(typeDecl);
164-
os << " _make() { return ";
165-
printer.printBaseName(typeDecl);
166-
os << "(); }\n";
172+
os << " _make() {";
173+
if (isOpaqueLayout) {
174+
os << "\n";
175+
os << " auto metadata = " << cxx_synthesis::getCxxImplNamespaceName()
176+
<< "::";
177+
printer.printSwiftTypeMetadataAccessFunctionCall(typeMetadataFuncName);
178+
os << ";\n";
179+
os << " return ";
180+
printer.printBaseName(typeDecl);
181+
os << "(";
182+
printer.printValueWitnessTableAccessFromTypeMetadata("metadata");
183+
os << ");\n }\n";
184+
} else {
185+
os << " return ";
186+
printer.printBaseName(typeDecl);
187+
os << "(); }\n";
188+
}
167189
// Print out the private accessors to the underlying Swift value storage.
168190
os << " inline const char * _Nonnull _getOpaquePointer() const { return "
169-
"_storage; }\n";
170-
os << " inline char * _Nonnull _getOpaquePointer() { return _storage; }\n";
191+
"_storage";
192+
if (isOpaqueLayout)
193+
os << ".getOpaquePointer()";
194+
os << "; }\n";
195+
os << " inline char * _Nonnull _getOpaquePointer() { return _storage";
196+
if (isOpaqueLayout)
197+
os << ".getOpaquePointer()";
198+
os << "; }\n";
171199
os << "\n";
172200

173201
// Print out the storage for the value type.
174-
os << " alignas(" << typeSizeAlign->alignment << ") ";
175-
os << "char _storage[" << typeSizeAlign->size << "];\n";
202+
os << " ";
203+
if (isOpaqueLayout) {
204+
printer.printSwiftImplQualifier();
205+
os << cxx_synthesis::getCxxOpaqueStorageClassName() << " _storage;\n";
206+
} else {
207+
os << "alignas(" << typeSizeAlign->alignment << ") ";
208+
os << "char _storage[" << typeSizeAlign->size << "];\n";
209+
}
176210
// Wrap up the value type.
177211
os << " friend class " << cxx_synthesis::getCxxImplNamespaceName() << "::";
178212
printCxxImplClassName(os, typeDecl);
@@ -209,7 +243,8 @@ void ClangValueTypePrinter::printValueTypeDecl(
209243
os << "};\n";
210244
});
211245

212-
printCValueTypeStorageStruct(cPrologueOS, typeDecl, *typeSizeAlign);
246+
if (!isOpaqueLayout)
247+
printCValueTypeStorageStruct(cPrologueOS, typeDecl, *typeSizeAlign);
213248
}
214249

215250
/// Print the name of the C stub struct for passing/returning a value type

lib/PrintAsClang/PrintSwiftToClangCoreScaffold.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,29 @@ static void printTypeMetadataResponseType(SwiftToClangInteropContext &ctx,
118118
funcSig.parameterTypes[0]);
119119
}
120120

121+
static void printSwiftResilientStorageClass(raw_ostream &os) {
122+
// FIXME: alignment.
123+
auto name = cxx_synthesis::getCxxOpaqueStorageClassName();
124+
os << "/// Container for an opaque Swift value, like resilient struct.\n";
125+
os << "class " << name << " {\n";
126+
os << "public:\n";
127+
os << " inline " << name << "() : storage(nullptr) { }\n";
128+
os << " inline " << name
129+
<< "(ValueWitnessTable * _Nonnull vwTable) : storage(new "
130+
"char[vwTable->size]) { }\n";
131+
os << " inline " << name << "(" << name
132+
<< "&& other) : storage(other.storage) { other.storage = nullptr; }\n";
133+
os << " inline " << name << "(const " << name << "&) = delete;\n";
134+
os << " inline ~" << name << "() { if (storage) { delete[] storage; } }\n";
135+
os << " inline char * _Nonnull getOpaquePointer() { return static_cast<char "
136+
"* _Nonnull>(storage); }\n";
137+
os << " inline const char * _Nonnull getOpaquePointer() const { return "
138+
"static_cast<char * _Nonnull>(storage); }\n";
139+
os << "private:\n";
140+
os << " char * _Nullable storage;\n";
141+
os << "};\n";
142+
}
143+
121144
void swift::printSwiftToClangCoreScaffold(SwiftToClangInteropContext &ctx,
122145
PrimitiveTypeMapping &typeMapping,
123146
raw_ostream &os) {
@@ -130,6 +153,8 @@ void swift::printSwiftToClangCoreScaffold(SwiftToClangInteropContext &ctx,
130153
os << "\n";
131154
printValueWitnessTable(os);
132155
});
156+
os << "\n";
157+
printSwiftResilientStorageClass(os);
133158
});
134159
});
135160
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@
5151
// CHECK-NEXT: }
5252
// CHECK-NEXT: #endif
5353
// CHECK-EMPTY:
54+
// CHECK-NEXT: /// Container for an opaque Swift value, like resilient struct.
55+
// CHECK-NEXT: class OpaqueStorage {
56+
// CHECK-NEXT: public:
57+
// CHECK-NEXT: inline OpaqueStorage() : storage(nullptr) { }
58+
// CHECK-NEXT: inline OpaqueStorage(ValueWitnessTable * _Nonnull vwTable) : storage(new char[vwTable->size]) { }
59+
// CHECK-NEXT: inline OpaqueStorage(OpaqueStorage&& other) : storage(other.storage) { other.storage = nullptr; }
60+
// CHECK-NEXT: inline OpaqueStorage(const OpaqueStorage&) = delete;
61+
// CHECK-NEXT: inline ~OpaqueStorage() { if (storage) { delete[] storage; } }
62+
// CHECK-NEXT: inline char * _Nonnull getOpaquePointer() { return static_cast<char * _Nonnull>(storage); }
63+
// CHECK-NEXT: inline const char * _Nonnull getOpaquePointer() const { return static_cast<char * _Nonnull>(storage); }
64+
// CHECK-NEXT: private:
65+
// CHECK-NEXT: char * _Nullable storage;
66+
// CHECK-NEXT: };
67+
// CHECK-EMPTY:
5468
// CHECK-NEXT: } // namespace _impl
5569
// CHECK-EMPTY:
5670
// CHECK-EMPTY:
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend %S/resilient-struct-in-cxx.swift -enable-library-evolution -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+
7+
// RUN: %target-interop-build-swift %S/resilient-struct-in-cxx.swift -enable-library-evolution -o %t/swift-structs-execution -Xlinker %t/swift-structs-execution.o -module-name Structs -Xfrontend -entry-point-function-name -Xfrontend swiftMain
8+
9+
// RUN: %target-codesign %t/swift-structs-execution
10+
// RUN: %target-run %t/swift-structs-execution | %FileCheck --check-prefixes=CHECK,CURRENT %s
11+
12+
// RUN: %target-interop-build-swift %S/resilient-struct-in-cxx.swift -enable-library-evolution -o %t/swift-structs-execution-new -Xlinker %t/swift-structs-execution.o -module-name Structs -Xfrontend -entry-point-function-name -Xfrontend swiftMain -D CHANGE_LAYOUT
13+
14+
// RUN: %target-codesign %t/swift-structs-execution-new
15+
// RUN: %target-run %t/swift-structs-execution-new | %FileCheck --check-prefixes=CHECK,CHANGE %s
16+
17+
// REQUIRES: executable_test
18+
19+
#include <assert.h>
20+
#include "structs.h"
21+
22+
int main() {
23+
using namespace Structs;
24+
25+
auto largeStruct = createLargeStruct(11);
26+
assert(largeStruct.getX1() == 11);
27+
largeStruct.dump();
28+
// CHECK: x.1 = 11, .2 = -11, .3 = 22, .4 = 7, .5 = 0
29+
30+
auto smallStruct = largeStruct.getFirstSmallStruct();
31+
assert(smallStruct.getX() == 65);
32+
smallStruct.dump();
33+
// CHECK: find - small dump
34+
// CURRENT-NEXT: x = 65
35+
// CHANGE-NEXT: x&y = 65&0
36+
37+
printSmallAndLarge(smallStruct, largeStruct);
38+
// CHECK: find - small dump
39+
// CURRENT-NEXT: x = 65
40+
// CHANGE-NEXT: x&y = 65&0
41+
// CHECK-NEXT: x.1 = 11, .2 = -11, .3 = 22, .4 = 7, .5 = 0
42+
43+
{
44+
auto structWithRefCountStoredProp =
45+
createStructWithRefCountStoredProp();
46+
structWithRefCountStoredProp.dump();
47+
}
48+
// CHECK-NEXT: create RefCountedClass 0
49+
// CHECK-NEXT: storedRef = 0
50+
// CHECK-NEXT: destroy RefCountedClass 0
51+
return 0;
52+
}

0 commit comments

Comments
 (0)