Skip to content

Commit e8e2a0f

Browse files
committed
[Interop][SwiftToCxx] Implement enum case switching
1 parent 2c425f0 commit e8e2a0f

File tree

6 files changed

+590
-28
lines changed

6 files changed

+590
-28
lines changed

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -384,30 +384,73 @@ class DeclAndTypePrinter::Implementation
384384
os << "@end\n";
385385
}
386386

387+
void visitEnumDeclCxx(EnumDecl *ED) {
388+
// FIXME: Print enum's availability
389+
ClangValueTypePrinter printer(os, owningPrinter.prologueOS,
390+
owningPrinter.typeMapping,
391+
owningPrinter.interopContext);
392+
printer.printValueTypeDecl(ED, /*bodyPrinter=*/[&]() {
393+
ClangSyntaxPrinter syntaxPrinter(os);
394+
llvm::SmallVector<const EnumElementDecl *> elements;
395+
396+
os << " enum class cases {\n";
397+
for (const auto *caseDecl : ED->getAllCases()) {
398+
for (const auto *element : caseDecl->getElements()) {
399+
os << " ";
400+
syntaxPrinter.printIdentifier(element->getNameStr());
401+
elements.push_back(element);
402+
os << ",\n";
403+
}
404+
}
405+
os << " };\n"; // enum class cases' closing bracket
406+
407+
if (elements.size() == 0) {
408+
os << "\n";
409+
return;
410+
}
411+
412+
// Rearrange cases so that they match their tag values
413+
llvm::stable_sort(elements, [&](const auto *e1, const auto *e2) {
414+
return e1->hasAssociatedValues() && !e2->hasAssociatedValues();
415+
});
416+
417+
// Printing operator cases()
418+
os << " inline operator cases() const {\n";
419+
os << " switch (_getEnumTag()) {\n";
420+
for (unsigned i = 0; i < elements.size(); ++i) {
421+
if (i != elements.size() - 1) {
422+
os << " case " << i << ": return cases::";
423+
} else {
424+
os << " case " << i << ": default: return cases::";
425+
}
426+
syntaxPrinter.printIdentifier(elements[i]->getNameStr());
427+
os << ";\n";
428+
}
429+
os << " }\n"; // switch's closing bracket
430+
os << " }\n"; // operator cases()'s closing bracket
431+
432+
// Printing predicates
433+
for (const auto *element : elements) {
434+
os << " inline bool is";
435+
auto name = element->getNameStr().str();
436+
// TODO: (tongjie) maybe find a reliable way for upper case conversion
437+
name[0] = std::toupper(name[0]);
438+
os << name << "() const {\n";
439+
os << " return *this == cases::";
440+
syntaxPrinter.printIdentifier(element->getNameStr());
441+
os << ";\n }\n";
442+
}
443+
os << "\n";
444+
});
445+
os << outOfLineDefinitions;
446+
outOfLineDefinitions.clear();
447+
}
448+
387449
void visitEnumDecl(EnumDecl *ED) {
388450
printDocumentationComment(ED);
389451

390452
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(ED, /*bodyPrinter=*/[&]() {
396-
ClangSyntaxPrinter syntaxPrinter(os);
397-
os << " enum class cases {";
398-
llvm::interleaveComma(
399-
ED->getAllCases(), os, [&](const EnumCaseDecl *caseDecl) {
400-
llvm::interleaveComma(caseDecl->getElements(), os,
401-
[&](const EnumElementDecl *elementDecl) {
402-
os << "\n ";
403-
syntaxPrinter.printIdentifier(
404-
elementDecl->getNameStr());
405-
});
406-
});
407-
os << "\n };\n";
408-
});
409-
os << outOfLineDefinitions;
410-
outOfLineDefinitions.clear();
453+
visitEnumDeclCxx(ED);
411454
return;
412455
}
413456

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,25 @@ void ClangValueTypePrinter::printValueTypeDecl(
202202
os << ".getOpaquePointer()";
203203
os << "; }\n";
204204
os << "\n";
205-
205+
// Print out helper function for getting enum tag for enum type
206+
if (isa<EnumDecl>(typeDecl)) {
207+
// FIXME: (tongjie) return type should be unsigned
208+
os << " inline int _getEnumTag() const {\n";
209+
os << " auto metadata = " << cxx_synthesis::getCxxImplNamespaceName()
210+
<< "::";
211+
printer.printSwiftTypeMetadataAccessFunctionCall(typeMetadataFuncName);
212+
os << ";\n";
213+
os << " auto *vwTable = ";
214+
printer.printValueWitnessTableAccessFromTypeMetadata("metadata");
215+
os << ";\n";
216+
os << " const auto *enumVWTable = reinterpret_cast<";
217+
ClangSyntaxPrinter(os).printSwiftImplQualifier();
218+
os << "EnumValueWitnessTable";
219+
os << " *>(vwTable);\n";
220+
os << " return enumVWTable->getEnumTag(_getOpaquePointer(), "
221+
"metadata._0);\n";
222+
os << " }\n";
223+
}
206224
// Print out the storage for the value type.
207225
os << " ";
208226
if (isOpaqueLayout) {

lib/PrintAsClang/PrintSwiftToClangCoreScaffold.cpp

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,12 @@ static void printKnownType(
6262
printKnownStruct(typeMapping, os, name, typeRecord);
6363
}
6464

65-
static void printValueWitnessTableFunctionType(raw_ostream &os, StringRef name,
66-
StringRef returnType,
67-
std::string paramTypes,
68-
uint16_t ptrauthDisc) {
69-
os << "using ValueWitness" << name << "Ty = " << returnType
65+
static void
66+
printValueWitnessTableFunctionType(raw_ostream &os, StringRef prefix,
67+
StringRef name, StringRef returnType,
68+
std::string paramTypes,
69+
uint16_t ptrauthDisc) {
70+
os << "using " << prefix << name << "Ty = " << returnType
7071
<< "(* __ptrauth_swift_value_witness_function_pointer(" << ptrauthDisc
7172
<< "))(" << paramTypes << ");\n";
7273
}
@@ -87,7 +88,7 @@ static void printValueWitnessTable(raw_ostream &os) {
8788
membersOS << " " << type << " " << #lowerId << ";\n";
8889
#define FUNCTION_VALUE_WITNESS(lowerId, upperId, returnType, paramTypes) \
8990
printValueWitnessTableFunctionType( \
90-
os, #upperId, returnType, makeParams paramTypes, \
91+
os, "ValueWitness", #upperId, returnType, makeParams paramTypes, \
9192
SpecialPointerAuthDiscriminators::upperId); \
9293
membersOS << " ValueWitness" << #upperId << "Ty _Nonnull " << #lowerId \
9394
<< ";\n";
@@ -102,7 +103,32 @@ static void printValueWitnessTable(raw_ostream &os) {
102103
#define VOID_TYPE "void"
103104
#include "swift/ABI/ValueWitness.def"
104105

105-
os << "\nstruct ValueWitnessTable {\n" << membersOS.str() << "};\n";
106+
os << "\nstruct ValueWitnessTable {\n" << membersOS.str() << "};\n\n";
107+
membersOS.str().clear();
108+
109+
#define WANT_ONLY_ENUM_VALUE_WITNESSES
110+
#define DATA_VALUE_WITNESS(lowerId, upperId, type) \
111+
membersOS << " " << type << " " << #lowerId << ";\n";
112+
#define FUNCTION_VALUE_WITNESS(lowerId, upperId, returnType, paramTypes) \
113+
printValueWitnessTableFunctionType( \
114+
os, "EnumValueWitness", #upperId, returnType, makeParams paramTypes, \
115+
SpecialPointerAuthDiscriminators::upperId); \
116+
membersOS << " EnumValueWitness" << #upperId << "Ty _Nonnull " << #lowerId \
117+
<< ";\n";
118+
#define MUTABLE_VALUE_TYPE "void * _Nonnull"
119+
#define IMMUTABLE_VALUE_TYPE "const void * _Nonnull"
120+
#define MUTABLE_BUFFER_TYPE "void * _Nonnull"
121+
#define IMMUTABLE_BUFFER_TYPE "const void * _Nonnull"
122+
#define TYPE_TYPE "void * _Nonnull"
123+
#define SIZE_TYPE "size_t"
124+
#define INT_TYPE "int"
125+
#define UINT_TYPE "unsigned"
126+
#define VOID_TYPE "void"
127+
#include "swift/ABI/ValueWitness.def"
128+
129+
os << "\nstruct EnumValueWitnessTable {\n"
130+
<< " ValueWitnessTable vwTable;\n"
131+
<< membersOS.str() << "};\n\n";
106132
}
107133

108134
static void printTypeMetadataResponseType(SwiftToClangInteropContext &ctx,

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@
4747
// CHECK-NEXT: unsigned extraInhabitantCount;
4848
// CHECK-NEXT: };
4949
// CHECK-EMPTY:
50+
// CHECK-NEXT: using EnumValueWitnessGetEnumTagTy = int(* __ptrauth_swift_value_witness_function_pointer(41909))(const void * _Nonnull, void * _Nonnull);
51+
// CHECK-NEXT: using EnumValueWitnessDestructiveProjectEnumDataTy = void(* __ptrauth_swift_value_witness_function_pointer(1053))(void * _Nonnull, void * _Nonnull);
52+
// CHECK-NEXT: using EnumValueWitnessDestructiveInjectEnumTagTy = void(* __ptrauth_swift_value_witness_function_pointer(45796))(void * _Nonnull, unsigned, void * _Nonnull);
53+
// CHECK-EMPTY:
54+
// CHECK-NEXT: struct EnumValueWitnessTable {
55+
// CHECK-NEXT: ValueWitnessTable vwTable;
56+
// CHECK-NEXT: EnumValueWitnessGetEnumTagTy _Nonnull getEnumTag;
57+
// CHECK-NEXT: EnumValueWitnessDestructiveProjectEnumDataTy _Nonnull destructiveProjectEnumData;
58+
// CHECK-NEXT: EnumValueWitnessDestructiveInjectEnumTagTy _Nonnull destructiveInjectEnumTag;
59+
// CHECK-NEXT: };
60+
// CHECK-EMPTY:
61+
// CHECK-EMPTY:
5062
// CHECK-NEXT: #ifdef __cplusplus
5163
// CHECK-NEXT: }
5264
// CHECK-NEXT: #endif
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend %S/enum-cxx-clike-enum-case-functions.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/enum-cxx-clike-enum-case-functions.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
10+
11+
// REQUIRES: executable_test
12+
13+
#include <cassert>
14+
#include "enums.h"
15+
16+
using namespace Enums;
17+
18+
void cxxCheckEnum(const DataCase &e) {
19+
assert(e.isOne());
20+
}
21+
22+
void cxxCheckEnum(const CLikeEnum &e) {
23+
switch (e) {
24+
case CLikeEnum::cases::one:
25+
assert(checkCLikeEnum(e, 1));
26+
assert(e.isOne());
27+
break;
28+
case CLikeEnum::cases::two:
29+
assert(checkCLikeEnum(e, 2));
30+
assert(e.isTwo());
31+
break;
32+
case CLikeEnum::cases::three:
33+
assert(checkCLikeEnum(e, 3));
34+
assert(e.isThree());
35+
break;
36+
}
37+
}
38+
39+
void cxxCheckEnum(const CharOrSectionMarker &e) {
40+
switch (e) {
41+
case CharOrSectionMarker::cases::Paragraph:
42+
assert(checkCharOrSectionMarker(e, 1));
43+
assert(e.isParagraph());
44+
break;
45+
case CharOrSectionMarker::cases::Char:
46+
assert(checkCharOrSectionMarker(e, 2));
47+
assert(e.isChar());
48+
break;
49+
case CharOrSectionMarker::cases::Chapter:
50+
assert(checkCharOrSectionMarker(e, 3));
51+
assert(e.isChapter());
52+
break;
53+
}
54+
}
55+
56+
void cxxCheckEnum(const IntOrInfinity &e) {
57+
switch (e) {
58+
case IntOrInfinity::cases::NegInfinity:
59+
assert(checkIntOrInfinity(e, 1));
60+
assert(e.isNegInfinity());
61+
break;
62+
case IntOrInfinity::cases::Int:
63+
assert(checkIntOrInfinity(e, 2));
64+
assert(e.isInt());
65+
break;
66+
case IntOrInfinity::cases::PosInfinity:
67+
assert(checkIntOrInfinity(e, 3));
68+
assert(e.isPosInfinity());
69+
break;
70+
}
71+
}
72+
73+
void cxxCheckEnum(const TerminalChar &e) {
74+
switch (e) {
75+
case TerminalChar::cases::Plain:
76+
assert(checkTerminalChar(e, 1));
77+
assert(e.isPlain());
78+
break;
79+
case TerminalChar::cases::Bold:
80+
assert(checkTerminalChar(e, 2));
81+
assert(e.isBold());
82+
break;
83+
case TerminalChar::cases::Underline:
84+
assert(checkTerminalChar(e, 3));
85+
assert(e.isUnderline());
86+
break;
87+
case TerminalChar::cases::Blink:
88+
assert(checkTerminalChar(e, 4));
89+
assert(e.isBlink());
90+
break;
91+
case TerminalChar::cases::Empty:
92+
assert(checkTerminalChar(e, 5));
93+
assert(e.isEmpty());
94+
break;
95+
case TerminalChar::cases::Cursor:
96+
assert(checkTerminalChar(e, 6));
97+
assert(e.isCursor());
98+
break;
99+
}
100+
}
101+
102+
void cxxCheckEnum(const IntDoubleOrBignum &e) {
103+
switch (e) {
104+
case IntDoubleOrBignum::cases::Int:
105+
assert(checkIntDoubleOrBignum(e, 1));
106+
assert(e.isInt());
107+
break;
108+
case IntDoubleOrBignum::cases::Double:
109+
assert(checkIntDoubleOrBignum(e, 2));
110+
assert(e.isDouble());
111+
break;
112+
case IntDoubleOrBignum::cases::Bignum:
113+
assert(checkIntDoubleOrBignum(e, 3));
114+
assert(e.isBignum());
115+
break;
116+
}
117+
}
118+
119+
int main() {
120+
{
121+
auto e1 = makeDataCase();
122+
cxxCheckEnum(e1);
123+
}
124+
125+
{
126+
auto e1 = makeCLikeEnum(1);
127+
auto e2 = makeCLikeEnum(2);
128+
auto e3 = makeCLikeEnum(3);
129+
130+
cxxCheckEnum(e1);
131+
cxxCheckEnum(e2);
132+
cxxCheckEnum(e3);
133+
}
134+
135+
{
136+
auto e1 = makeCharOrSectionMarker(1);
137+
auto e2 = makeCharOrSectionMarker(2);
138+
auto e3 = makeCharOrSectionMarker(3);
139+
140+
cxxCheckEnum(e1);
141+
cxxCheckEnum(e2);
142+
cxxCheckEnum(e3);
143+
}
144+
145+
{
146+
auto e1 = makeIntOrInfinity(1);
147+
auto e2 = makeIntOrInfinity(2);
148+
auto e3 = makeIntOrInfinity(3);
149+
150+
cxxCheckEnum(e1);
151+
cxxCheckEnum(e2);
152+
cxxCheckEnum(e3);
153+
}
154+
155+
{
156+
auto e1 = makeTerminalChar(1);
157+
auto e2 = makeTerminalChar(2);
158+
auto e3 = makeTerminalChar(3);
159+
auto e4 = makeTerminalChar(4);
160+
auto e5 = makeTerminalChar(5);
161+
auto e6 = makeTerminalChar(6);
162+
163+
cxxCheckEnum(e1);
164+
cxxCheckEnum(e2);
165+
cxxCheckEnum(e3);
166+
cxxCheckEnum(e4);
167+
cxxCheckEnum(e5);
168+
cxxCheckEnum(e6);
169+
}
170+
171+
{
172+
auto e1 = makeIntDoubleOrBignum(1);
173+
auto e2 = makeIntDoubleOrBignum(2);
174+
auto e3 = makeIntDoubleOrBignum(3);
175+
176+
cxxCheckEnum(e1);
177+
cxxCheckEnum(e2);
178+
cxxCheckEnum(e3);
179+
}
180+
return 0;
181+
}

0 commit comments

Comments
 (0)