Skip to content

Commit 773a043

Browse files
committed
[interop][SwiftToCxx] extract generic associated value from enum
1 parent 07337cd commit 773a043

File tree

5 files changed

+149
-46
lines changed

5 files changed

+149
-46
lines changed

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -455,28 +455,24 @@ class DeclAndTypePrinter::Implementation
455455
associatedValueList->size() > 1) {
456456
return;
457457
}
458-
auto paramType = associatedValueList->front()->getType();
459-
Type objectType;
460-
OptionalTypeKind optKind;
461-
std::tie(objectType, optKind) = getObjectTypeAndOptionality(
462-
paramType->getNominalOrBoundGenericNominal(), paramType);
463-
auto objectTypeDecl = objectType->getNominalOrBoundGenericNominal();
458+
auto paramType = associatedValueList->front()->getInterfaceType();
464459

465460
std::string declName, defName, name;
466461
llvm::raw_string_ostream declOS(declName), defOS(defName), nameOS(name);
467462
ClangSyntaxPrinter(nameOS).printIdentifier(elementDecl->getNameStr());
468463
name[0] = std::toupper(name[0]);
469464

470-
if (ED->isGeneric())
471-
return;
472465
clangFuncPrinter.printCustomCxxFunction(
473466
{paramType},
474467
[&](auto &types) {
475468
// Printing function name and return type
476469
os << " inline " << types[paramType] << " get" << name;
470+
outOfLineSyntaxPrinter
471+
.printNominalTypeOutsideMemberDeclTemplateSpecifiers(ED);
477472
outOfLineOS << " inline " << types[paramType] << ' ';
478-
outOfLineSyntaxPrinter.printBaseName(ED);
479-
outOfLineOS << "::get" << name;
473+
outOfLineSyntaxPrinter.printNominalTypeQualifier(
474+
ED, /*moduleContext=*/ED->getModuleContext());
475+
outOfLineOS << "get" << name;
480476
},
481477
[&](auto &types) {}, true,
482478
[&](auto &types) {
@@ -492,6 +488,20 @@ class DeclAndTypePrinter::Implementation
492488
outOfLineOS << "(*this);\n";
493489
outOfLineOS << " char * _Nonnull payloadFromDestruction = "
494490
"thisCopy->_destructiveProjectEnumData();\n";
491+
if (const auto *gtpt = paramType->getAs<GenericTypeParamType>()) {
492+
DeclAndTypeClangFunctionPrinter::printGenericReturnSequence(
493+
outOfLineOS, gtpt, [](StringRef) {},
494+
/*initializeWithTake=*/StringRef("payloadFromDestruction"));
495+
return;
496+
}
497+
// FIXME: unify non-generic return with regular function emission
498+
// return path.
499+
Type objectType;
500+
OptionalTypeKind optKind;
501+
std::tie(objectType, optKind) = getObjectTypeAndOptionality(
502+
paramType->getNominalOrBoundGenericNominal(), paramType);
503+
auto objectTypeDecl = objectType->getNominalOrBoundGenericNominal();
504+
495505
if (auto knownCxxType =
496506
owningPrinter.typeMapping.getKnownCxxTypeInfo(
497507
objectTypeDecl)) {

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 67 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,72 @@ void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
912912
namePrinter();
913913
}
914914

915+
void DeclAndTypeClangFunctionPrinter::printGenericReturnSequence(
916+
raw_ostream &os, const GenericTypeParamType *gtpt,
917+
llvm::function_ref<void(StringRef)> invocationPrinter,
918+
Optional<StringRef> initializeWithTakeFromValue) {
919+
std::string returnAddress;
920+
llvm::raw_string_ostream ros(returnAddress);
921+
ros << "reinterpret_cast<void *>(&returnValue)";
922+
std::string resultTyName;
923+
{
924+
llvm::raw_string_ostream os(resultTyName);
925+
ClangSyntaxPrinter(os).printGenericTypeParamTypeName(gtpt);
926+
}
927+
928+
os << " if constexpr (std::is_base_of<::swift::"
929+
<< cxx_synthesis::getCxxImplNamespaceName() << "::RefCountedClass, "
930+
<< resultTyName << ">::value) {\n";
931+
if (!initializeWithTakeFromValue) {
932+
os << " void *returnValue;\n ";
933+
invocationPrinter(/*additionalParam=*/StringRef(ros.str()));
934+
os << ";\n";
935+
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
936+
<< "::implClassFor<" << resultTyName
937+
<< ">::type::makeRetained(returnValue);\n";
938+
} else {
939+
// FIXME: support taking a class pointer.
940+
os << "abort();\n";
941+
}
942+
os << " } else if constexpr (::swift::"
943+
<< cxx_synthesis::getCxxImplNamespaceName() << "::isValueType<"
944+
<< resultTyName << ">) {\n";
945+
946+
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
947+
<< "::implClassFor<" << resultTyName
948+
<< ">::type::returnNewValue([&](void * _Nonnull returnValue) {\n";
949+
if (!initializeWithTakeFromValue) {
950+
invocationPrinter(/*additionalParam=*/StringRef("returnValue"));
951+
} else {
952+
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
953+
<< "::implClassFor<" << resultTyName
954+
<< ">::type::initializeWithTake(reinterpret_cast<char * "
955+
"_Nonnull>(returnValue), "
956+
<< *initializeWithTakeFromValue << ")";
957+
}
958+
os << ";\n });\n";
959+
os << " } else if constexpr (::swift::"
960+
<< cxx_synthesis::getCxxImplNamespaceName() << "::isSwiftBridgedCxxRecord<"
961+
<< resultTyName << ">) {\n";
962+
if (!initializeWithTakeFromValue) {
963+
ClangTypeHandler::printGenericReturnScaffold(os, resultTyName,
964+
invocationPrinter);
965+
} else {
966+
// FIXME: support taking a C++ record type.
967+
os << "abort();\n";
968+
}
969+
os << " } else {\n";
970+
os << " " << resultTyName << " returnValue;\n";
971+
if (!initializeWithTakeFromValue) {
972+
invocationPrinter(/*additionalParam=*/StringRef(ros.str()));
973+
} else {
974+
os << "memcpy(&returnValue, " << *initializeWithTakeFromValue
975+
<< ", sizeof(returnValue))";
976+
}
977+
os << ";\n return returnValue;\n";
978+
os << " }\n";
979+
}
980+
915981
void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
916982
const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature,
917983
StringRef swiftSymbolName, const ModuleDecl *moduleContext, Type resultTy,
@@ -1012,42 +1078,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
10121078
if (!isKnownCxxType(resultTy, typeMapping) &&
10131079
!hasKnownOptionalNullableCxxMapping(resultTy)) {
10141080
if (const auto *gtpt = resultTy->getAs<GenericTypeParamType>()) {
1015-
std::string returnAddress;
1016-
llvm::raw_string_ostream ros(returnAddress);
1017-
ros << "reinterpret_cast<void *>(&returnValue)";
1018-
std::string resultTyName;
1019-
{
1020-
llvm::raw_string_ostream os(resultTyName);
1021-
ClangSyntaxPrinter(os).printGenericTypeParamTypeName(gtpt);
1022-
}
1023-
1024-
os << " if constexpr (std::is_base_of<::swift::"
1025-
<< cxx_synthesis::getCxxImplNamespaceName() << "::RefCountedClass, "
1026-
<< resultTyName << ">::value) {\n";
1027-
os << " void *returnValue;\n ";
1028-
printCallToCFunc(/*additionalParam=*/StringRef(ros.str()));
1029-
os << ";\n";
1030-
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
1031-
<< "::implClassFor<" << resultTyName
1032-
<< ">::type::makeRetained(returnValue);\n";
1033-
os << " } else if constexpr (::swift::"
1034-
<< cxx_synthesis::getCxxImplNamespaceName() << "::isValueType<"
1035-
<< resultTyName << ">) {\n";
1036-
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
1037-
<< "::implClassFor<" << resultTyName
1038-
<< ">::type::returnNewValue([&](void * _Nonnull returnValue) {\n";
1039-
printCallToCFunc(/*additionalParam=*/StringRef("returnValue"));
1040-
os << ";\n });\n";
1041-
os << " } else if constexpr (::swift::"
1042-
<< cxx_synthesis::getCxxImplNamespaceName()
1043-
<< "::isSwiftBridgedCxxRecord<" << resultTyName << ">) {\n";
1044-
ClangTypeHandler::printGenericReturnScaffold(os, resultTyName,
1045-
printCallToCFunc);
1046-
os << " } else {\n";
1047-
os << " " << resultTyName << " returnValue;\n";
1048-
printCallToCFunc(/*additionalParam=*/StringRef(ros.str()));
1049-
os << ";\n return returnValue;\n";
1050-
os << " }\n";
1081+
printGenericReturnSequence(os, gtpt, printCallToCFunc);
10511082
return;
10521083
}
10531084
if (auto *classDecl = resultTy->getClassOrBoundGenericClass()) {

lib/PrintAsClang/PrintClangFunction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class AbstractFunctionDecl;
3030
class AccessorDecl;
3131
class AnyFunctionType;
3232
class FuncDecl;
33+
class GenericTypeParamType;
3334
class ModuleDecl;
3435
class NominalTypeDecl;
3536
class LoweredFunctionSignature;
@@ -135,6 +136,11 @@ class DeclAndTypeClangFunctionPrinter {
135136
ModuleDecl *moduleContext,
136137
OutputLanguageMode outputLang);
137138

139+
static void printGenericReturnSequence(
140+
raw_ostream &os, const GenericTypeParamType *gtpt,
141+
llvm::function_ref<void(StringRef)> invocationPrinter,
142+
Optional<StringRef> initializeWithTakeFromValue = llvm::None);
143+
138144
using PrinterTy =
139145
llvm::function_ref<void(llvm::MapVector<Type, std::string> &)>;
140146

test/Interop/SwiftToCxx/generics/generic-enum-execution.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "generics.h"
1414
#include <cassert>
15+
#include <cstdio>
1516

1617
int main() {
1718
using namespace Generics;
@@ -29,7 +30,11 @@ int main() {
2930
x.method();
3031
// CHECK-NEXT: GenericOpt<T>::testme::some(-11);
3132
assert(x.getComputedProp() == 42);
33+
assert(x.isSome());
34+
auto val = x.getSome();
35+
assert(val == -11);
3236
x.reset();
37+
assert(x.isNone());
3338
assert(x.genericMethod<double>(5.25) == 5.25);
3439
// CHECK-NEXT: GenericOpt<T>::genericMethod<T>::none,5.25;
3540
}
@@ -46,9 +51,21 @@ int main() {
4651
x.method();
4752
// CHECK-NEXT: GenericOpt<T>::testme::some(2250);
4853
assert(x.getComputedProp() == 42);
54+
assert(x.isSome());
55+
auto val = x.getSome();
56+
assert(val == 2250);
4957
x.reset();
58+
assert(x.isNone());
5059
assert(x.genericMethod<double>(-1.25) == -1.25);
5160
// CHECK-NEXT: GenericOpt<T>::genericMethod<T>::none,-1.25;
5261
}
62+
{
63+
auto x = makeGenericOpt<StructForEnum>(StructForEnum::init());
64+
auto val = x.getSome();
65+
// CHECK-NEXT: init-TracksDeinit
66+
// CHECK-NEXT: destroy-TracksDeinit
67+
}
68+
puts("EOF");
69+
// CHECK-NEXT: EOF
5370
return 0;
5471
}

test/Interop/SwiftToCxx/generics/generic-enum-in-cxx.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,24 @@
1010

1111
// FIXME: remove the need for -Wno-reserved-identifier
1212

13+
class TracksDeinit {
14+
init() {
15+
print("init-TracksDeinit")
16+
}
17+
deinit {
18+
print("destroy-TracksDeinit")
19+
}
20+
}
21+
22+
@frozen
23+
public struct StructForEnum {
24+
let x: TracksDeinit
25+
26+
public init() {
27+
x = TracksDeinit()
28+
}
29+
}
30+
1331
@frozen public enum GenericOpt<T> {
1432
case none
1533
case some(T)
@@ -153,6 +171,27 @@ public func inoutConcreteOpt(_ x: inout GenericOpt<UInt16>) {
153171
// CHECK-NEXT: inline bool GenericOpt<T_0_0>::isSome() const {
154172
// CHECK-NEXT: return *this == GenericOpt<T_0_0>::some;
155173
// CHECK-NEXT: }
174+
// CHECK-NEXT: template<class T_0_0>
175+
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
176+
// CHECK-NEXT: inline T_0_0 GenericOpt<T_0_0>::getSome() const {
177+
// CHECK-NEXT: if (!isSome()) abort();
178+
// CHECK-NEXT: alignas(GenericOpt) unsigned char buffer[sizeof(GenericOpt)];
179+
// CHECK-NEXT: auto *thisCopy = new(buffer) GenericOpt(*this);
180+
// CHECK-NEXT: char * _Nonnull payloadFromDestruction = thisCopy->_destructiveProjectEnumData();
181+
// CHECK-NEXT: if constexpr (std::is_base_of<::swift::_impl::RefCountedClass, T_0_0>::value) {
182+
// CHECK-NEXT: abort();
183+
// CHECK-NEXT: } else if constexpr (::swift::_impl::isValueType<T_0_0>) {
184+
// CHECK-NEXT: return ::swift::_impl::implClassFor<T_0_0>::type::returnNewValue([&](void * _Nonnull returnValue) {
185+
// CHECK-NEXT: return ::swift::_impl::implClassFor<T_0_0>::type::initializeWithTake(reinterpret_cast<char * _Nonnull>(returnValue), payloadFromDestruction);
186+
// CHECK-NEXT: });
187+
// CHECK-NEXT: } else if constexpr (::swift::_impl::isSwiftBridgedCxxRecord<T_0_0>) {
188+
// CHECK-NEXT: abort();
189+
// CHECK-NEXT: } else {
190+
// CHECK-NEXT: T_0_0 returnValue;
191+
// CHECK-NEXT: memcpy(&returnValue, payloadFromDestruction, sizeof(returnValue));
192+
// CHECK-NEXT: return returnValue;
193+
// CHECK-NEXT: }
194+
// CHECK-NEXT: }
156195
// CHECK-NEXT: template<class T_0_0>
157196
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
158197
// CHECK-NEXT: inline bool GenericOpt<T_0_0>::isNone() const {

0 commit comments

Comments
 (0)