Skip to content

Commit ebd8dea

Browse files
committed
[interop][SwiftToCxx] return generic structs without concrete type specialization
1 parent 45d209f commit ebd8dea

File tree

5 files changed

+110
-25
lines changed

5 files changed

+110
-25
lines changed

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ using namespace swift;
3232

3333
namespace {
3434

35-
enum class FunctionSignatureTypeUse { ParamType, ReturnType };
35+
// FIXME: RENAME.
36+
enum class FunctionSignatureTypeUse { TypeReference, ParamType, ReturnType };
3637

3738
Optional<PrimitiveTypeMapping::ClangTypeInfo>
3839
getKnownTypeInfo(const TypeDecl *typeDecl, PrimitiveTypeMapping &typeMapping,
@@ -68,7 +69,7 @@ bool isResilientType(Type t) {
6869
return false;
6970
}
7071

71-
bool isGenericType(Type t) { return t->is<GenericTypeParamType>(); }
72+
bool isGenericType(Type t) { return t->hasTypeParameter(); }
7273

7374
bool isKnownCxxType(Type t, PrimitiveTypeMapping &typeMapping) {
7475
return isKnownType(t, typeMapping, OutputLanguageMode::Cxx);
@@ -79,6 +80,10 @@ bool isKnownCType(Type t, PrimitiveTypeMapping &typeMapping) {
7980
}
8081

8182
struct CFunctionSignatureTypePrinterModifierDelegate {
83+
/// Prefix the initially printed value type.
84+
Optional<llvm::function_ref<ClangValueTypePrinter::TypeUseKind(
85+
ClangValueTypePrinter::TypeUseKind)>>
86+
mapValueTypeUseKind = None;
8287
/// Prefix the indirect value type / class type param being printed in C mode.
8388
Optional<llvm::function_ref<void(raw_ostream &)>>
8489
prefixIndirectlyPassedParamTypeInC = None;
@@ -185,20 +190,24 @@ class CFunctionSignatureTypePrinter
185190
ClangRepresentation visitEnumType(EnumType *ET,
186191
Optional<OptionalTypeKind> optionalKind,
187192
bool isInOutParam) {
188-
return visitValueType(ET, optionalKind, isInOutParam);
193+
return visitValueType(ET->getNominalOrBoundGenericNominal(), ET,
194+
optionalKind, isInOutParam);
189195
}
190196

191197
ClangRepresentation visitStructType(StructType *ST,
192198
Optional<OptionalTypeKind> optionalKind,
193199
bool isInOutParam) {
194-
return visitValueType(ST, optionalKind, isInOutParam);
200+
return visitValueType(ST->getNominalOrBoundGenericNominal(), ST,
201+
optionalKind, isInOutParam);
195202
}
196203

197-
ClangRepresentation visitValueType(NominalType *NT,
204+
ClangRepresentation visitValueType(const NominalTypeDecl *decl,
205+
NominalType *NT,
198206
Optional<OptionalTypeKind> optionalKind,
199-
bool isInOutParam) {
200-
assert(isa<StructType>(NT) || isa<EnumType>(NT));
201-
const auto *decl = NT->getNominalOrBoundGenericNominal();
207+
bool isInOutParam,
208+
ArrayRef<Type> genericArgs = {}) {
209+
if (NT)
210+
assert(isa<StructType>(NT) || isa<EnumType>(NT));
202211
assert(isa<StructDecl>(decl) || isa<EnumDecl>(decl));
203212

204213
// Handle known type names.
@@ -211,7 +220,7 @@ class CFunctionSignatureTypePrinter
211220
if (typeUseKind == FunctionSignatureTypeUse::ParamType) {
212221
if (languageMode != OutputLanguageMode::Cxx &&
213222
(decl->isResilient() ||
214-
interopContext.getIrABIDetails().shouldPassIndirectly(NT))) {
223+
(NT && interopContext.getIrABIDetails().shouldPassIndirectly(NT)))) {
215224
if (modifiersDelegate.prefixIndirectlyPassedParamTypeInC)
216225
(*modifiersDelegate.prefixIndirectlyPassedParamTypeInC)(os);
217226
// FIXME: it would be nice to print out the C struct type here.
@@ -226,9 +235,30 @@ class CFunctionSignatureTypePrinter
226235
.printValueTypeParameterType(decl, languageMode, moduleContext,
227236
isInOutParam);
228237
}
229-
} else
238+
} else {
230239
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
231-
.printValueTypeReturnType(decl, languageMode, moduleContext);
240+
.printValueTypeReturnType(
241+
decl, languageMode,
242+
modifiersDelegate.mapValueTypeUseKind
243+
? (*modifiersDelegate.mapValueTypeUseKind)(
244+
ClangValueTypePrinter::TypeUseKind::CxxTypeName)
245+
: ClangValueTypePrinter::TypeUseKind::CxxTypeName,
246+
moduleContext);
247+
if (!genericArgs.empty()) {
248+
os << '<';
249+
llvm::SaveAndRestore<FunctionSignatureTypeUse> typeUseNormal(
250+
typeUseKind, FunctionSignatureTypeUse::TypeReference);
251+
decltype(modifiersDelegate) emptyModifiersDelegate;
252+
llvm::SaveAndRestore<decltype(modifiersDelegate)> modReset(
253+
modifiersDelegate, emptyModifiersDelegate);
254+
ClangRepresentation result = ClangRepresentation::representable;
255+
llvm::interleaveComma(genericArgs, os, [&](Type t) {
256+
result.merge(visitPart(t, None, false));
257+
});
258+
os << '>';
259+
return result;
260+
}
261+
}
232262
return ClangRepresentation::representable;
233263
}
234264

@@ -262,15 +292,17 @@ class CFunctionSignatureTypePrinter
262292
bool isInOutParam) {
263293
if (printIfKnownGenericStruct(BGT, optionalKind, isInOutParam))
264294
return ClangRepresentation::representable;
265-
return ClangRepresentation::unsupported;
295+
return visitValueType(BGT->getDecl(), nullptr, optionalKind, isInOutParam,
296+
BGT->getGenericArgs());
266297
}
267298

268299
ClangRepresentation
269300
visitGenericTypeParamType(GenericTypeParamType *genericTpt,
270301
Optional<OptionalTypeKind> optionalKind,
271302
bool isInOutParam) {
272303
// FIXME: handle optionalKind.
273-
if (typeUseKind == FunctionSignatureTypeUse::ReturnType) {
304+
if (typeUseKind == FunctionSignatureTypeUse::ReturnType ||
305+
typeUseKind == FunctionSignatureTypeUse::TypeReference) {
274306
// generic is always returned indirectly in C signature.
275307
assert(languageMode == OutputLanguageMode::Cxx);
276308
os << genericTpt->getName();
@@ -628,7 +660,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
628660
// indirectly by a pointer.
629661
if (!isKnownCxxType(resultTy, typeMapping) &&
630662
!hasKnownOptionalNullableCxxMapping(resultTy)) {
631-
if (isGenericType(resultTy)) {
663+
if (resultTy->is<GenericTypeParamType>()) {
632664
std::string returnAddress;
633665
llvm::raw_string_ostream ros(returnAddress);
634666
ros << "reinterpret_cast<void *>(&returnValue)";
@@ -666,13 +698,29 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
666698
if (auto *decl = resultTy->getNominalOrBoundGenericNominal()) {
667699
if ((isa<StructDecl>(decl) || isa<EnumDecl>(decl))) {
668700
bool isIndirect =
669-
decl->isResilient() ||
701+
decl->isResilient() || isGenericType(resultTy) ||
670702
interopContext.getIrABIDetails().shouldReturnIndirectly(resultTy);
671703
ClangValueTypePrinter valueTypePrinter(os, cPrologueOS, typeMapping,
672704
interopContext);
673705
if (isIndirect) {
674706
valueTypePrinter.printValueTypeIndirectReturnScaffold(
675-
decl, moduleContext, [&](StringRef returnParam) {
707+
decl, moduleContext,
708+
[&]() {
709+
CFunctionSignatureTypePrinterModifierDelegate delegate;
710+
delegate
711+
.mapValueTypeUseKind = [](ClangValueTypePrinter::TypeUseKind
712+
kind) {
713+
return ClangValueTypePrinter::TypeUseKind::CxxImplTypeName;
714+
};
715+
CFunctionSignatureTypePrinter typePrinter(
716+
os, cPrologueOS, typeMapping, OutputLanguageMode::Cxx,
717+
interopContext, delegate, moduleContext, declPrinter,
718+
FunctionSignatureTypeUse::TypeReference);
719+
auto result =
720+
typePrinter.visit(resultTy, None, /*isInOut=*/false);
721+
assert(!result.isUnsupported());
722+
},
723+
[&](StringRef returnParam) {
676724
printCallToCFunc(/*additionalParam=*/returnParam);
677725
});
678726
} else {

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ printCValueTypeStorageStruct(raw_ostream &os, const NominalTypeDecl *typeDecl,
8686

8787
void ClangValueTypePrinter::forwardDeclType(raw_ostream &os,
8888
const NominalTypeDecl *typeDecl) {
89+
if (typeDecl->isGeneric()) {
90+
auto genericSignature =
91+
typeDecl->getGenericSignature().getCanonicalSignature();
92+
ClangSyntaxPrinter(os).printGenericSignature(genericSignature);
93+
}
8994
os << "class ";
9095
ClangSyntaxPrinter(os).printBaseName(typeDecl);
9196
os << ";\n";
@@ -513,10 +518,19 @@ void ClangValueTypePrinter::printParameterCxxToCUseScaffold(
513518

514519
void ClangValueTypePrinter::printValueTypeReturnType(
515520
const NominalTypeDecl *type, OutputLanguageMode outputLang,
516-
const ModuleDecl *moduleContext) {
521+
TypeUseKind typeUse, const ModuleDecl *moduleContext) {
517522
assert(isa<StructDecl>(type) || isa<EnumDecl>(type));
523+
// FIXME: make a type use.
518524
if (outputLang == OutputLanguageMode::Cxx) {
519-
printCxxTypeName(os, type, moduleContext);
525+
if (typeUse == TypeUseKind::CxxTypeName)
526+
printCxxTypeName(os, type, moduleContext);
527+
else {
528+
assert(typeUse == TypeUseKind::CxxImplTypeName);
529+
ClangSyntaxPrinter(os).printModuleNamespaceQualifiersIfNeeded(
530+
type->getModuleContext(), moduleContext);
531+
os << cxx_synthesis::getCxxImplNamespaceName() << "::";
532+
printCxxImplClassName(os, type);
533+
}
520534
} else {
521535
os << "struct ";
522536
printCStubTypeName(type);
@@ -525,13 +539,11 @@ void ClangValueTypePrinter::printValueTypeReturnType(
525539

526540
void ClangValueTypePrinter::printValueTypeIndirectReturnScaffold(
527541
const NominalTypeDecl *type, const ModuleDecl *moduleContext,
542+
llvm::function_ref<void()> typePrinter,
528543
llvm::function_ref<void(StringRef)> bodyPrinter) {
529544
assert(isa<StructDecl>(type) || isa<EnumDecl>(type));
530545
os << " return ";
531-
ClangSyntaxPrinter(os).printModuleNamespaceQualifiersIfNeeded(
532-
type->getModuleContext(), moduleContext);
533-
os << cxx_synthesis::getCxxImplNamespaceName() << "::";
534-
printCxxImplClassName(os, type);
546+
typePrinter();
535547
os << "::returnNewValue([&](void * _Nonnull result) {\n ";
536548
bodyPrinter("result");
537549
os << ";\n";

lib/PrintAsClang/PrintClangValueType.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,27 @@ class ClangValueTypePrinter {
5757
llvm::function_ref<void()> cxxParamPrinter,
5858
bool isInOut, bool isSelf);
5959

60+
enum class TypeUseKind {
61+
// The name of the C++ class that corresponds to the Swift value type (with
62+
// any qualifiers).
63+
CxxTypeName,
64+
// The name of the C++ _impl class that corresponds to the Swift value type
65+
// (with any qualifiers).
66+
CxxImplTypeName
67+
};
68+
6069
/// Print the return type that refers to a Swift struct type in C/C++.
6170
void printValueTypeReturnType(const NominalTypeDecl *typeDecl,
6271
OutputLanguageMode outputLang,
72+
TypeUseKind typeUse,
6373
const ModuleDecl *moduleContext);
6474

6575
/// Print the supporting code that's required to indirectly return a C++
6676
/// class that represents a Swift value type as it's being indirectly passed
6777
/// from the C function that represents the native Swift function.
6878
void printValueTypeIndirectReturnScaffold(
6979
const NominalTypeDecl *typeDecl, const ModuleDecl *moduleContext,
80+
llvm::function_ref<void()> typePrinter,
7081
llvm::function_ref<void(StringRef)> bodyPrinter);
7182

7283
/// Print the supporting code that's required to directly return a C++ class

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ extern "C" void puts(const char *);
1818
int main() {
1919
using namespace Generics;
2020

21-
auto x = _impl::_impl_GenericPair<int, int>::returnNewValue([] (void *p) {
22-
// nada.
23-
});
21+
auto x = makeGenericPair<int, int>(11, 42);
2422
puts("no\n");
2523
// CHECK: no
2624
return 0;

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ public struct GenericPair<T, T2> {
1414
let y: T2
1515
}
1616

17+
public func makeGenericPair<T, T1>(_ x: T, _ y: T1) -> GenericPair<T, T1> {
18+
return GenericPair<T, T1>(x: x, y: y);
19+
}
20+
1721
// CHECK: template<class T_0_0, class T_0_1>
1822
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
1923
// CHECK-NEXT: class _impl_GenericPair;
@@ -44,3 +48,15 @@ public struct GenericPair<T, T2> {
4448
// CHECK-NEXT: callable(result._getOpaquePointer());
4549
// CHECK-NEXT: return result;
4650
// CHECK-NEXT: }
51+
52+
// CHECK: template<class T_0_0, class T_0_1>
53+
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
54+
// CHECK-NEXT: class GenericPair;
55+
// CHECK-EMPTY:
56+
// CHECK-NEXT: template<class T, class T1>
57+
// CHECK-NEXT: requires swift::isUsableInGenericContext<T> && swift::isUsableInGenericContext<T1>
58+
// CHECK-NEXT: inline GenericPair<T, T1> makeGenericPair(const T & x, const T1 & y) noexcept SWIFT_WARN_UNUSED_RESULT {
59+
// CHECK-NEXT: return _impl::_impl_GenericPair<T, T1>::returnNewValue([&](void * _Nonnull result) {
60+
// CHECK-NEXT: _impl::$s8Generics15makeGenericPairyAA0cD0Vyxq_Gx_q_tr0_lF(result, swift::_impl::getOpaquePointer(x), swift::_impl::getOpaquePointer(y), swift::getTypeMetadata<T>(), swift::getTypeMetadata<T1>());
61+
// CHECK-NEXT: });
62+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)