Skip to content

Commit fbd8f0e

Browse files
authored
Merge pull request #60472 from hyp/eng/generic-value-types
[interop][SwiftToCxx] pass&return value types to/from generic functions
2 parents 035bee6 + d23a64e commit fbd8f0e

13 files changed

+327
-119
lines changed

lib/PrintAsClang/PrintClangClassType.cpp

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -94,47 +94,8 @@ void ClangClassTypePrinter::printClassTypeDecl(
9494
os << "};\n";
9595
});
9696

97-
// FIXME: avoid popping out of the module's namespace here.
98-
os << "} // end namespace \n\n";
99-
os << "namespace swift {\n";
100-
101-
os << "#pragma clang diagnostic push\n";
102-
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
103-
os << "template<>\n";
104-
os << "static inline const constexpr bool isUsableInGenericContext<";
105-
printer.printBaseName(typeDecl->getModuleContext());
106-
os << "::";
107-
printer.printBaseName(typeDecl);
108-
os << "> = true;\n";
109-
os << "#pragma clang diagnostic pop\n";
110-
111-
os << "template<>\n";
112-
os << "inline void * _Nonnull getTypeMetadata<";
113-
printer.printBaseName(typeDecl->getModuleContext());
114-
os << "::";
115-
printer.printBaseName(typeDecl);
116-
os << ">() {\n";
117-
os << " return ";
118-
printer.printBaseName(typeDecl->getModuleContext());
119-
os << "::" << cxx_synthesis::getCxxImplNamespaceName()
120-
<< "::" << typeMetadataFuncName << "(0)._0;\n";
121-
os << "}\n";
122-
os << "namespace " << cxx_synthesis::getCxxImplNamespaceName() << "{\n";
123-
os << "template<>\n";
124-
os << "struct implClassFor<";
125-
printer.printBaseName(typeDecl->getModuleContext());
126-
os << "::";
127-
printer.printBaseName(typeDecl);
128-
os << "> { using type = ";
129-
printer.printBaseName(typeDecl->getModuleContext());
130-
os << "::" << cxx_synthesis::getCxxImplNamespaceName() << "::";
131-
printCxxImplClassName(os, typeDecl);
132-
os << "; };\n";
133-
os << "} // namespace\n";
134-
os << "} // namespace swift\n";
135-
os << "\nnamespace ";
136-
printer.printBaseName(typeDecl->getModuleContext());
137-
os << " {\n";
97+
ClangValueTypePrinter::printTypeGenericTraits(os, typeDecl,
98+
typeMetadataFuncName);
13899
}
139100

140101
void ClangClassTypePrinter::printClassTypeReturnScaffold(

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -451,13 +451,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
451451
if (type->getAs<ArchetypeType>() && type->getAs<ArchetypeType>()
452452
->getInterfaceType()
453453
->is<GenericTypeParamType>()) {
454-
// FIXME: NEED to handle boxed resilient type.
455-
// os << "swift::" << cxx_synthesis::getCxxImplNamespaceName() <<
456-
// "::getOpaquePointer(";
457-
os << "reinterpret_cast<";
458-
if (!isInOut)
459-
os << "const ";
460-
os << "void *>(&";
454+
os << "swift::" << cxx_synthesis::getCxxImplNamespaceName()
455+
<< "::getOpaquePointer(";
461456
namePrinter();
462457
os << ')';
463458
return;
@@ -576,10 +571,10 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
576571
if (!isKnownCxxType(resultTy, typeMapping) &&
577572
!hasKnownOptionalNullableCxxMapping(resultTy)) {
578573
if (isGenericType(resultTy)) {
579-
// FIXME: Support returning value types.
580574
std::string returnAddress;
581575
llvm::raw_string_ostream ros(returnAddress);
582576
ros << "reinterpret_cast<void *>(&returnValue)";
577+
StringRef resultTyName = "T"; // FIXME
583578

584579
os << " if constexpr (std::is_base_of<::swift::"
585580
<< cxx_synthesis::getCxxImplNamespaceName()
@@ -589,6 +584,14 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
589584
os << ";\n";
590585
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
591586
<< "::implClassFor<T>::type::makeRetained(returnValue);\n";
587+
os << " } else if constexpr (::swift::"
588+
<< cxx_synthesis::getCxxImplNamespaceName() << "::isValueType<"
589+
<< resultTyName << ">) {\n";
590+
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
591+
<< "::implClassFor<" << resultTyName
592+
<< ">::type::returnNewValue([&](void * _Nonnull returnValue) {\n";
593+
printCallToCFunc(/*additionalParam=*/StringRef("returnValue"));
594+
os << ";\n });\n";
592595
os << " } else {\n";
593596
os << " T returnValue;\n";
594597
printCallToCFunc(/*additionalParam=*/StringRef(ros.str()));

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ void ClangValueTypePrinter::printValueTypeDecl(
152152
if (isOpaqueLayout) {
153153
os << " _storage = ";
154154
printer.printSwiftImplQualifier();
155-
os << cxx_synthesis::getCxxOpaqueStorageClassName() << "(vwTable);\n";
155+
os << cxx_synthesis::getCxxOpaqueStorageClassName()
156+
<< "(vwTable->size, vwTable->getAlignment());\n";
156157
}
157158
os << " vwTable->initializeWithCopy(_getOpaquePointer(), const_cast<char "
158159
"*>(other._getOpaquePointer()), metadata._0);\n";
@@ -172,10 +173,12 @@ void ClangValueTypePrinter::printValueTypeDecl(
172173
// Print out private default constructor.
173174
os << " inline ";
174175
printer.printBaseName(typeDecl);
176+
// FIXME: make noexcept.
175177
if (isOpaqueLayout) {
176178
os << "(";
177179
printer.printSwiftImplQualifier();
178-
os << "ValueWitnessTable * _Nonnull vwTable) : _storage(vwTable) {}\n";
180+
os << "ValueWitnessTable * _Nonnull vwTable) : _storage(vwTable->size, "
181+
"vwTable->getAlignment()) {}\n";
179182
} else {
180183
os << "() {}\n";
181184
}
@@ -281,6 +284,8 @@ void ClangValueTypePrinter::printValueTypeDecl(
281284

282285
if (!isOpaqueLayout)
283286
printCValueTypeStorageStruct(cPrologueOS, typeDecl, *typeSizeAlign);
287+
288+
printTypeGenericTraits(os, typeDecl, typeMetadataFuncName);
284289
}
285290

286291
/// Print the name of the C stub struct for passing/returning a value type
@@ -456,3 +461,68 @@ void ClangValueTypePrinter::printValueTypeDirectReturnScaffold(
456461
os << ");\n";
457462
os << " });\n";
458463
}
464+
465+
void ClangValueTypePrinter::printTypeGenericTraits(
466+
raw_ostream &os, const NominalTypeDecl *typeDecl,
467+
StringRef typeMetadataFuncName) {
468+
ClangSyntaxPrinter printer(os);
469+
// FIXME: avoid popping out of the module's namespace here.
470+
os << "} // end namespace \n\n";
471+
os << "namespace swift {\n";
472+
473+
os << "#pragma clang diagnostic push\n";
474+
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
475+
os << "template<>\n";
476+
os << "static inline const constexpr bool isUsableInGenericContext<";
477+
printer.printBaseName(typeDecl->getModuleContext());
478+
os << "::";
479+
printer.printBaseName(typeDecl);
480+
os << "> = true;\n";
481+
os << "template<>\n";
482+
os << "inline void * _Nonnull getTypeMetadata<";
483+
printer.printBaseName(typeDecl->getModuleContext());
484+
os << "::";
485+
printer.printBaseName(typeDecl);
486+
os << ">() {\n";
487+
os << " return ";
488+
printer.printBaseName(typeDecl->getModuleContext());
489+
os << "::" << cxx_synthesis::getCxxImplNamespaceName()
490+
<< "::" << typeMetadataFuncName << "(0)._0;\n";
491+
os << "}\n";
492+
493+
os << "namespace " << cxx_synthesis::getCxxImplNamespaceName() << "{\n";
494+
495+
if (!isa<ClassDecl>(typeDecl)) {
496+
os << "template<>\n";
497+
os << "static inline const constexpr bool isValueType<";
498+
printer.printBaseName(typeDecl->getModuleContext());
499+
os << "::";
500+
printer.printBaseName(typeDecl);
501+
os << "> = true;\n";
502+
if (typeDecl->isResilient()) {
503+
os << "template<>\n";
504+
os << "static inline const constexpr bool isOpaqueLayout<";
505+
printer.printBaseName(typeDecl->getModuleContext());
506+
os << "::";
507+
printer.printBaseName(typeDecl);
508+
os << "> = true;\n";
509+
}
510+
}
511+
512+
os << "template<>\n";
513+
os << "struct implClassFor<";
514+
printer.printBaseName(typeDecl->getModuleContext());
515+
os << "::";
516+
printer.printBaseName(typeDecl);
517+
os << "> { using type = ";
518+
printer.printBaseName(typeDecl->getModuleContext());
519+
os << "::" << cxx_synthesis::getCxxImplNamespaceName() << "::";
520+
printCxxImplClassName(os, typeDecl);
521+
os << "; };\n";
522+
os << "} // namespace\n";
523+
os << "#pragma clang diagnostic pop\n";
524+
os << "} // namespace swift\n";
525+
os << "\nnamespace ";
526+
printer.printBaseName(typeDecl->getModuleContext());
527+
os << " {\n";
528+
}

lib/PrintAsClang/PrintClangValueType.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ class ClangValueTypePrinter {
9494
StringRef metadataVarName = "metadata",
9595
StringRef vwTableVarName = "vwTable");
9696

97+
static void printTypeGenericTraits(raw_ostream &os,
98+
const NominalTypeDecl *typeDecl,
99+
StringRef typeMetadataFuncName);
100+
97101
private:
98102
/// Prints out the C stub name used to pass/return value directly for the
99103
/// given value type.

lib/PrintAsClang/PrintSwiftToClangCoreScaffold.cpp

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ static void printValueWitnessTable(raw_ostream &os) {
111111
#define VOID_TYPE "void"
112112
#include "swift/ABI/ValueWitness.def"
113113

114+
membersOS << "\n constexpr size_t getAlignment() const { return (flags & "
115+
<< TargetValueWitnessFlags<uint64_t>::AlignmentMask << ") + 1; }\n";
114116
os << "\nstruct ValueWitnessTable {\n" << membersOS.str() << "};\n\n";
115117
membersOS.str().clear();
116118

@@ -154,43 +156,7 @@ static void printTypeMetadataResponseType(SwiftToClangInteropContext &ctx,
154156
funcSig.parameterTypes[0]);
155157
}
156158

157-
static void printSwiftResilientStorageClass(raw_ostream &os) {
158-
auto name = cxx_synthesis::getCxxOpaqueStorageClassName();
159-
static_assert(TargetValueWitnessFlags<uint64_t>::AlignmentMask ==
160-
TargetValueWitnessFlags<uint32_t>::AlignmentMask,
161-
"alignment mask doesn't match");
162-
os << "/// Container for an opaque Swift value, like resilient struct.\n";
163-
os << "class " << name << " {\n";
164-
os << "public:\n";
165-
os << " inline " << name << "() noexcept : storage(nullptr) { }\n";
166-
os << " inline " << name
167-
<< "(ValueWitnessTable * _Nonnull vwTable) noexcept : storage("
168-
"reinterpret_cast<char *>(opaqueAlloc(vwTable->size, (vwTable->flags &"
169-
<< TargetValueWitnessFlags<uint64_t>::AlignmentMask << ") + 1))) { }\n";
170-
os << " inline " << name << "(" << name
171-
<< "&& other) noexcept : storage(other.storage) { other.storage = "
172-
"nullptr; }\n";
173-
os << " inline " << name << "(const " << name << "&) noexcept = delete;\n";
174-
os << " inline ~" << name
175-
<< "() noexcept { if (storage) { opaqueFree(static_cast<char "
176-
"* _Nonnull>(storage)); } }\n";
177-
os << " void operator =(" << name
178-
<< "&& other) noexcept { auto temp = storage; storage = other.storage; "
179-
"other.storage = temp; }\n";
180-
os << " void operator =(const " << name << "&) noexcept = delete;\n";
181-
os << " inline char * _Nonnull getOpaquePointer() noexcept { return "
182-
"static_cast<char "
183-
"* _Nonnull>(storage); }\n";
184-
os << " inline const char * _Nonnull getOpaquePointer() const noexcept { "
185-
"return "
186-
"static_cast<char * _Nonnull>(storage); }\n";
187-
os << "private:\n";
188-
os << " char * _Nullable storage;\n";
189-
os << "};\n";
190-
}
191-
192159
void printCxxNaiveException(raw_ostream &os) {
193-
os << "\n";
194160
os << "/// Naive exception class that should be thrown\n";
195161
os << "class NaiveException {\n";
196162
os << "public:\n";
@@ -273,7 +239,6 @@ void swift::printSwiftToClangCoreScaffold(SwiftToClangInteropContext &ctx,
273239
/*isCForwardDefinition=*/true);
274240
});
275241
os << "\n";
276-
printSwiftResilientStorageClass(os);
277242
printCxxNaiveException(os);
278243
});
279244
os << "\n";

stdlib/public/SwiftShims/_SwiftCxxInteroperability.h

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,42 @@ inline void opaqueFree(void *_Nonnull p) noexcept {
5353
#endif
5454
}
5555

56+
/// Base class for a container for an opaque Swift value, like resilient struct.
57+
class OpaqueStorage {
58+
public:
59+
inline OpaqueStorage() noexcept : storage(nullptr) {}
60+
inline OpaqueStorage(size_t size, size_t alignment) noexcept
61+
: storage(reinterpret_cast<char *>(opaqueAlloc(size, alignment))) {}
62+
inline OpaqueStorage(OpaqueStorage &&other) noexcept
63+
: storage(other.storage) {
64+
other.storage = nullptr;
65+
}
66+
inline OpaqueStorage(const OpaqueStorage &) noexcept = delete;
67+
68+
inline ~OpaqueStorage() noexcept {
69+
if (storage) {
70+
opaqueFree(static_cast<char *_Nonnull>(storage));
71+
}
72+
}
73+
74+
void operator=(OpaqueStorage &&other) noexcept {
75+
auto temp = storage;
76+
storage = other.storage;
77+
other.storage = temp;
78+
}
79+
void operator=(const OpaqueStorage &) noexcept = delete;
80+
81+
inline char *_Nonnull getOpaquePointer() noexcept {
82+
return static_cast<char *_Nonnull>(storage);
83+
}
84+
inline const char *_Nonnull getOpaquePointer() const noexcept {
85+
return static_cast<char *_Nonnull>(storage);
86+
}
87+
88+
private:
89+
char *_Nullable storage;
90+
};
91+
5692
/// Base class for a Swift reference counted class value.
5793
class RefCountedClass {
5894
public:
@@ -104,8 +140,6 @@ using UInt = size_t;
104140
template <class T>
105141
static inline const constexpr bool isUsableInGenericContext = false;
106142

107-
#pragma clang diagnostic pop
108-
109143
/// Returns the type metadat for the given Swift type T.
110144
template <class T> inline void *_Nonnull getTypeMetadata();
111145

@@ -117,8 +151,31 @@ template <class T> struct implClassFor {
117151
// using type = ...;
118152
};
119153

154+
/// True if the given type is a Swift value type.
155+
template <class T> static inline const constexpr bool isValueType = false;
156+
157+
/// True if the given type is a Swift value type with opaque layout that can be
158+
/// boxed.
159+
template <class T> static inline const constexpr bool isOpaqueLayout = false;
160+
161+
/// Returns the opaque pointer to the given value.
162+
template <class T>
163+
inline const void *_Nonnull getOpaquePointer(const T &value) {
164+
if constexpr (isOpaqueLayout<T>)
165+
return reinterpret_cast<const OpaqueStorage &>(value).getOpaquePointer();
166+
return reinterpret_cast<const void *>(&value);
167+
}
168+
169+
template <class T> inline void *_Nonnull getOpaquePointer(T &value) {
170+
if constexpr (isOpaqueLayout<T>)
171+
return reinterpret_cast<OpaqueStorage &>(value).getOpaquePointer();
172+
return reinterpret_cast<void *>(&value);
173+
}
174+
120175
} // namespace _impl
121176

177+
#pragma clang diagnostic pop
178+
122179
} // namespace swift
123180
#endif
124181

test/Interop/SwiftToCxx/class/swift-class-in-cxx.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ public final class ClassWithIntField {
6565
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
6666
// CHECK-NEXT: template<>
6767
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Class::ClassWithIntField> = true;
68-
// CHECK-NEXT: #pragma clang diagnostic pop
6968
// CHECK-NEXT: template<>
7069
// CHECK-NEXT: inline void * _Nonnull getTypeMetadata<Class::ClassWithIntField>() {
7170
// CHECK-NEXT: return Class::_impl::$s5Class0A12WithIntFieldCMa(0)._0;
@@ -74,6 +73,7 @@ public final class ClassWithIntField {
7473
// CHECK-NEXT: template<>
7574
// CHECK-NEXT: struct implClassFor<Class::ClassWithIntField> { using type = Class::_impl::_impl_ClassWithIntField; };
7675
// CHECK-NEXT: } // namespace
76+
// CHECK-NEXT: #pragma clang diagnostic pop
7777
// CHECK-NEXT: } // namespace swift
7878
// CHECK-EMPTY:
7979
// CHECK-NEXT: namespace Class {

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

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
// CHECK-NEXT: size_t stride;
5252
// CHECK-NEXT: unsigned flags;
5353
// CHECK-NEXT: unsigned extraInhabitantCount;
54+
// CHECK-EMPTY:
55+
// CHECK-NEXT: constexpr size_t getAlignment() const { return (flags & 255) + 1; }
5456
// CHECK-NEXT: };
5557
// CHECK-EMPTY:
5658
// CHECK-NEXT: using EnumValueWitnessGetEnumTagTy = int(* __ptrauth_swift_value_witness_function_pointer(41909))(const void * _Nonnull, void * _Nonnull) SWIFT_NOEXCEPT_FUNCTION_PTR;
@@ -96,22 +98,6 @@
9698
// CHECK-NEXT: }
9799
// CHECK-NEXT: #endif
98100
// CHECK-EMPTY:
99-
// CHECK-NEXT: /// Container for an opaque Swift value, like resilient struct.
100-
// CHECK-NEXT: class OpaqueStorage {
101-
// CHECK-NEXT: public:
102-
// CHECK-NEXT: inline OpaqueStorage() noexcept : storage(nullptr) { }
103-
// CHECK-NEXT: inline OpaqueStorage(ValueWitnessTable * _Nonnull vwTable) noexcept : storage(reinterpret_cast<char *>(opaqueAlloc(vwTable->size, (vwTable->flags &255) + 1))) { }
104-
// CHECK-NEXT: inline OpaqueStorage(OpaqueStorage&& other) noexcept : storage(other.storage) { other.storage = nullptr; }
105-
// CHECK-NEXT: inline OpaqueStorage(const OpaqueStorage&) noexcept = delete;
106-
// CHECK-NEXT: inline ~OpaqueStorage() noexcept { if (storage) { opaqueFree(static_cast<char * _Nonnull>(storage)); } }
107-
// CHECK-NEXT: void operator =(OpaqueStorage&& other) noexcept { auto temp = storage; storage = other.storage; other.storage = temp; }
108-
// CHECK-NEXT: void operator =(const OpaqueStorage&) noexcept = delete;
109-
// CHECK-NEXT: inline char * _Nonnull getOpaquePointer() noexcept { return static_cast<char * _Nonnull>(storage); }
110-
// CHECK-NEXT: inline const char * _Nonnull getOpaquePointer() const noexcept { return static_cast<char * _Nonnull>(storage); }
111-
// CHECK-NEXT: private:
112-
// CHECK-NEXT: char * _Nullable storage;
113-
// CHECK-NEXT: };
114-
// CHECK-EMPTY:
115101
// CHECK-NEXT: /// Naive exception class that should be thrown
116102
// CHECK-NEXT: class NaiveException {
117103
// CHECK-NEXT: public:

0 commit comments

Comments
 (0)