Skip to content

Commit c6ff96f

Browse files
authored
Merge pull request #59584 from WANGJIEKE/cxx-interop-enum-pass-return
[interop][SwiftToCxx] Add initial support for passing/returning enums
2 parents 56024b3 + db8940c commit c6ff96f

9 files changed

+387
-35
lines changed

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,19 @@ class DeclAndTypePrinter::Implementation
386386

387387
void visitEnumDecl(EnumDecl *ED) {
388388
printDocumentationComment(ED);
389+
390+
if (outputLang == OutputLanguageMode::Cxx) {
391+
// FIXME: Print enum's availability
392+
ClangValueTypePrinter printer(os, owningPrinter.prologueOS,
393+
owningPrinter.typeMapping,
394+
owningPrinter.interopContext);
395+
printer.printValueTypeDecl(
396+
ED, /*bodyPrinter=*/[&]() {}); // TODO: (tongjie) print cases
397+
os << outOfLineDefinitions;
398+
outOfLineDefinitions.clear();
399+
return;
400+
}
401+
389402
os << "typedef ";
390403
StringRef customName = getNameForObjC(ED, CustomNamesOnly);
391404
if (customName.empty()) {

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,12 @@ class ModuleWriter {
240240
}
241241

242242
void forwardDeclare(const EnumDecl *ED) {
243+
// TODO: skip for now; will overhaul the forward decals for c++ in the
244+
// future
245+
if (outputLangMode == swift::OutputLanguageMode::Cxx) {
246+
return;
247+
}
248+
243249
assert(ED->isObjC() || ED->hasClangNode());
244250

245251
forwardDeclare(ED, [&]{
@@ -595,7 +601,9 @@ class ModuleWriter {
595601
const Decl *D = declsToWrite.back();
596602
bool success = true;
597603

598-
if (outputLangMode == OutputLanguageMode::Cxx) {
604+
if (auto ED = dyn_cast<EnumDecl>(D)) {
605+
success = writeEnum(ED);
606+
} else if (outputLangMode == OutputLanguageMode::Cxx) {
599607
if (auto FD = dyn_cast<FuncDecl>(D))
600608
success = writeFunc(FD);
601609
if (auto SD = dyn_cast<StructDecl>(D))
@@ -606,8 +614,6 @@ class ModuleWriter {
606614
success = writeClass(CD);
607615
else if (auto PD = dyn_cast<ProtocolDecl>(D))
608616
success = writeProtocol(PD);
609-
else if (auto ED = dyn_cast<EnumDecl>(D))
610-
success = writeEnum(ED);
611617
else if (auto ED = dyn_cast<FuncDecl>(D))
612618
success = writeFunc(ED);
613619
else

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -133,18 +133,30 @@ class CFunctionSignatureTypePrinter
133133
visitPart(aliasTy->getSinglyDesugaredType(), optionalKind, isInOutParam);
134134
}
135135

136+
void visitEnumType(EnumType *ET, Optional<OptionalTypeKind> optionalKind,
137+
bool isInOutParam) {
138+
visitValueType(ET, optionalKind, isInOutParam);
139+
}
140+
136141
void visitStructType(StructType *ST, Optional<OptionalTypeKind> optionalKind,
137142
bool isInOutParam) {
138-
const StructDecl *SD = ST->getStructOrBoundGenericStruct();
143+
visitValueType(ST, optionalKind, isInOutParam);
144+
}
145+
146+
void visitValueType(NominalType *NT, Optional<OptionalTypeKind> optionalKind,
147+
bool isInOutParam) {
148+
assert(isa<StructType>(NT) || isa<EnumType>(NT));
149+
const auto *decl = NT->getNominalOrBoundGenericNominal();
150+
assert(isa<StructDecl>(decl) || isa<EnumDecl>(decl));
139151

140152
// Handle known type names.
141-
if (printIfKnownSimpleType(SD, optionalKind, isInOutParam))
153+
if (printIfKnownSimpleType(decl, optionalKind, isInOutParam))
142154
return;
143155
// FIXME: Handle optional structures.
144156
if (typeUseKind == FunctionSignatureTypeUse::ParamType) {
145157
if (languageMode != OutputLanguageMode::Cxx &&
146-
(SD->isResilient() ||
147-
interopContext.getIrABIDetails().shouldPassIndirectly(ST))) {
158+
(decl->isResilient() ||
159+
interopContext.getIrABIDetails().shouldPassIndirectly(NT))) {
148160
if (modifiersDelegate.prefixIndirectParamValueTypeInC)
149161
(*modifiersDelegate.prefixIndirectParamValueTypeInC)(os);
150162
// FIXME: it would be nice to print out the C struct type here.
@@ -156,11 +168,11 @@ class CFunctionSignatureTypePrinter
156168

157169
} else {
158170
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
159-
.printValueTypeParameterType(SD, languageMode, isInOutParam);
171+
.printValueTypeParameterType(decl, languageMode, isInOutParam);
160172
}
161173
} else
162174
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
163-
.printValueTypeReturnType(SD, languageMode);
175+
.printValueTypeReturnType(decl, languageMode);
164176
}
165177

166178
void visitPart(Type Ty, Optional<OptionalTypeKind> optionalKind,
@@ -297,16 +309,19 @@ void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
297309
Type type, StringRef name, bool isInOut, bool isIndirect,
298310
llvm::Optional<AdditionalParam::Role> paramRole) {
299311
auto namePrinter = [&]() { ClangSyntaxPrinter(os).printIdentifier(name); };
300-
if (!isKnownCxxType(type, typeMapping)) {
301-
if (auto *structDecl = type->getStructOrBoundGenericStruct()) {
302-
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
303-
.printParameterCxxToCUseScaffold(
304-
isIndirect || structDecl->isResilient() ||
305-
interopContext.getIrABIDetails().shouldPassIndirectly(type),
306-
structDecl, namePrinter, isInOut,
307-
/*isSelf=*/paramRole &&
308-
*paramRole == AdditionalParam::Role::Self);
309-
return;
312+
if (!isKnownCxxType(type, typeMapping) &&
313+
!hasKnownOptionalNullableCxxMapping(type)) {
314+
if (auto *decl = type->getNominalOrBoundGenericNominal()) {
315+
if ((isa<StructDecl>(decl) || isa<EnumDecl>(decl))) {
316+
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
317+
.printParameterCxxToCUseScaffold(
318+
isIndirect || decl->isResilient() ||
319+
interopContext.getIrABIDetails().shouldPassIndirectly(type),
320+
decl, namePrinter, isInOut,
321+
/*isSelf=*/paramRole &&
322+
*paramRole == AdditionalParam::Role::Self);
323+
return;
324+
}
310325
}
311326
}
312327
// Primitive types are passed directly without any conversions.
@@ -368,23 +383,26 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
368383

369384
// Values types are returned either direcly in their C representation, or
370385
// indirectly by a pointer.
371-
if (!isKnownCxxType(resultTy, typeMapping)) {
372-
if (auto *structDecl = resultTy->getStructOrBoundGenericStruct()) {
373-
bool isIndirect =
374-
structDecl->isResilient() ||
375-
interopContext.getIrABIDetails().shouldReturnIndirectly(resultTy);
376-
ClangValueTypePrinter valueTypePrinter(os, cPrologueOS, typeMapping,
377-
interopContext);
378-
if (isIndirect) {
379-
valueTypePrinter.printValueTypeIndirectReturnScaffold(
380-
structDecl, [&](StringRef returnParam) {
381-
printCallToCFunc(/*additionalParam=*/returnParam);
382-
});
383-
} else {
384-
valueTypePrinter.printValueTypeDirectReturnScaffold(
385-
structDecl, [&]() { printCallToCFunc(/*additionalParam=*/None); });
386+
if (!isKnownCxxType(resultTy, typeMapping) &&
387+
!hasKnownOptionalNullableCxxMapping(resultTy)) {
388+
if (auto *decl = resultTy->getNominalOrBoundGenericNominal()) {
389+
if ((isa<StructDecl>(decl) || isa<EnumDecl>(decl))) {
390+
bool isIndirect =
391+
decl->isResilient() ||
392+
interopContext.getIrABIDetails().shouldReturnIndirectly(resultTy);
393+
ClangValueTypePrinter valueTypePrinter(os, cPrologueOS, typeMapping,
394+
interopContext);
395+
if (isIndirect) {
396+
valueTypePrinter.printValueTypeIndirectReturnScaffold(
397+
decl, [&](StringRef returnParam) {
398+
printCallToCFunc(/*additionalParam=*/returnParam);
399+
});
400+
} else {
401+
valueTypePrinter.printValueTypeDirectReturnScaffold(
402+
decl, [&]() { printCallToCFunc(/*additionalParam=*/None); });
403+
}
404+
return;
386405
}
387-
return;
388406
}
389407
}
390408

@@ -476,3 +494,14 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
476494
/*isIndirect=*/false}});
477495
os << " }\n";
478496
}
497+
498+
bool DeclAndTypeClangFunctionPrinter::hasKnownOptionalNullableCxxMapping(
499+
Type type) {
500+
if (auto optionalObjectType = type->getOptionalObjectType()) {
501+
if (auto typeInfo = typeMapping.getKnownCxxTypeInfo(
502+
optionalObjectType->getNominalOrBoundGenericNominal())) {
503+
return typeInfo->canBeNullable;
504+
}
505+
}
506+
return false;
507+
}

lib/PrintAsClang/PrintClangFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class DeclAndTypeClangFunctionPrinter {
102102
Type type, StringRef name, bool isInOut, bool isIndirect = false,
103103
llvm::Optional<AdditionalParam::Role> paramRole = None);
104104

105+
bool hasKnownOptionalNullableCxxMapping(Type type);
106+
105107
raw_ostream &os;
106108
raw_ostream &cPrologueOS;
107109
PrimitiveTypeMapping &typeMapping;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend %S/large-enums-pass-return-in-cxx.swift -typecheck -module-name Enums -clang-header-expose-public-decls -emit-clang-header-path %t/enums.h
4+
5+
// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-enums-execution.o
6+
// RUN: %target-interop-build-swift %S/large-enums-pass-return-in-cxx.swift -o %t/swift-enums-execution -Xlinker %t/swift-enums-execution.o -module-name Enums -Xfrontend -entry-point-function-name -Xfrontend swiftMain
7+
8+
// RUN: %target-codesign %t/swift-enums-execution
9+
// RUN: %target-run %t/swift-enums-execution | %FileCheck %s
10+
11+
// REQUIRES: executable_test
12+
// UNSUPPORTED: CPU=arm64e
13+
14+
#include <cassert>
15+
#include <cstdint>
16+
#include "enums.h"
17+
18+
int main() {
19+
using namespace Enums;
20+
21+
// sizeof(generated cxx class) = 1 + max(sizeof(case) for all cases) + padding
22+
static_assert(sizeof(Large) == 56, "MemoryLayout<Large>.stride == 56");
23+
24+
auto large = makeLarge(-1);
25+
printLarge(large);
26+
// CHECK: Large.second
27+
inoutLarge(large, 10);
28+
printLarge(large);
29+
// CHECK: Large.first(-1, -2, -3, -4, -5, -6)
30+
printLarge(passThroughLarge(large));
31+
// CHECK: Large.first(-1, -2, -3, -4, -5, -6)
32+
33+
return 0;
34+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -typecheck -module-name Enums -clang-header-expose-public-decls -emit-clang-header-path %t/enums.h
3+
// RUN: %FileCheck %s < %t/enums.h
4+
5+
// RUN: %check-interop-cxx-header-in-clang(%t/enums.h -Wno-unused-private-field -Wno-unused-function)
6+
7+
public enum Large {
8+
case first(Int64, Int64, Int64, Int64, Int64, Int64)
9+
case second
10+
}
11+
12+
public func makeLarge(_ x: Int) -> Large {
13+
return x >= 0 ? .first(0, 1, 2, 3, 4, 5) : .second
14+
}
15+
16+
public func printLarge(_ en: Large) {
17+
switch en {
18+
case let .first(a, b, c, d, e, f):
19+
let x = (a, b, c, d, e, f)
20+
print("Large.first\(x)")
21+
case .second:
22+
print("Large.second")
23+
}
24+
}
25+
26+
public func passThroughLarge(_ en: Large) -> Large {
27+
return en
28+
}
29+
30+
public func inoutLarge(_ en: inout Large, _ x: Int) {
31+
if x >= 0 {
32+
en = .first(-1, -2, -3, -4, -5, -6)
33+
} else {
34+
en = .second
35+
}
36+
}
37+
38+
// CHECK: SWIFT_EXTERN void $s5Enums10inoutLargeyyAA0C0Oz_SitF(void * _Nonnull en, ptrdiff_t x) SWIFT_NOEXCEPT SWIFT_CALL; // inoutLarge(_:_:)
39+
// CHECK: SWIFT_EXTERN void $s5Enums9makeLargeyAA0C0OSiF(SWIFT_INDIRECT_RESULT void * _Nonnull, ptrdiff_t x) SWIFT_NOEXCEPT SWIFT_CALL; // makeLarge(_:)
40+
// CHECK: SWIFT_EXTERN void $s5Enums16passThroughLargeyAA0D0OADF(SWIFT_INDIRECT_RESULT void * _Nonnull, const void * _Nonnull en) SWIFT_NOEXCEPT SWIFT_CALL; // passThroughLarge(_:)
41+
// CHECK: SWIFT_EXTERN void $s5Enums10printLargeyyAA0C0OF(const void * _Nonnull en) SWIFT_NOEXCEPT SWIFT_CALL; // printLarge(_:)
42+
// CHECK: class Large final {
43+
44+
// CHECK: inline void inoutLarge(Large& en, swift::Int x) noexcept {
45+
// CHECK-NEXT: return _impl::$s5Enums10inoutLargeyyAA0C0Oz_SitF(_impl::_impl_Large::getOpaquePointer(en), x);
46+
// CHECK-NEXT: }
47+
48+
// CHECK: inline Large makeLarge(swift::Int x) noexcept SWIFT_WARN_UNUSED_RESULT {
49+
// CHECK-NEXT: return _impl::_impl_Large::returnNewValue([&](void * _Nonnull result) {
50+
// CHECK-NEXT: _impl::$s5Enums9makeLargeyAA0C0OSiF(result, x);
51+
// CHECK-NEXT: });
52+
// CHECK-NEXT: }
53+
54+
// CHECK: inline Large passThroughLarge(const Large& en) noexcept SWIFT_WARN_UNUSED_RESULT {
55+
// CHECK-NEXT: return _impl::_impl_Large::returnNewValue([&](void * _Nonnull result) {
56+
// CHECK-NEXT: _impl::$s5Enums16passThroughLargeyAA0D0OADF(result, _impl::_impl_Large::getOpaquePointer(en));
57+
// CHECK-NEXT: });
58+
// CHECK-NEXT: }
59+
60+
// CHECK: inline void printLarge(const Large& en) noexcept {
61+
// CHECK-NEXT: return _impl::$s5Enums10printLargeyyAA0C0OF(_impl::_impl_Large::getOpaquePointer(en));
62+
// CHECK-NEXT: }
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend %S/small-enums-pass-return-in-cxx.swift -typecheck -module-name Enums -clang-header-expose-public-decls -emit-clang-header-path %t/enums.h
4+
5+
// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-enums-execution.o
6+
// RUN: %target-interop-build-swift %S/small-enums-pass-return-in-cxx.swift -o %t/swift-enums-execution -Xlinker %t/swift-enums-execution.o -module-name Enums -Xfrontend -entry-point-function-name -Xfrontend swiftMain
7+
8+
// RUN: %target-codesign %t/swift-enums-execution
9+
// RUN: %target-run %t/swift-enums-execution | %FileCheck %s
10+
11+
// REQUIRES: executable_test
12+
// UNSUPPORTED: CPU=arm64e
13+
14+
#include <cassert>
15+
#include <cstddef>
16+
#include "enums.h"
17+
18+
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
19+
20+
int main() {
21+
using namespace Enums;
22+
23+
// sizeof(generated cxx class) = 1 + max(sizeof(case) for all cases) + padding
24+
static_assert(sizeof(Tiny) == 1, "MemoryLayout<Tiny>.stride == 1");
25+
static_assert(sizeof(Small) == 16, "MemoryLayout<Small>.stride == 16");
26+
27+
auto tiny = makeTiny(10);
28+
printTiny(tiny);
29+
// CHECK: Tiny.first
30+
inoutTiny(tiny, -1);
31+
printTiny(tiny);
32+
// CHECK: Tiny.second
33+
printTiny(passThroughTiny(tiny));
34+
// CHECK: Tiny.second
35+
36+
auto small = makeSmall(-3);
37+
printSmall(small);
38+
// CHECK: Small.second(3.0)
39+
inoutSmall(small, 100);
40+
printSmall(small);
41+
// CHECK: Small.first(100)
42+
printSmall(passThroughSmall(small));
43+
// CHECK: Small.first(100)
44+
45+
return 0;
46+
}

0 commit comments

Comments
 (0)