Skip to content

Commit 482e1ac

Browse files
committed
[Interop][SwiftToCxx] Implement the new enum header design
1 parent 7024813 commit 482e1ac

File tree

6 files changed

+463
-158
lines changed

6 files changed

+463
-158
lines changed

docs/CppInteroperability/UserGuide-CallingSwiftFromC++.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ enum value will abort the program.
595595
A resilient Swift enumeration value could represent a case that's unknown to the client.
596596
Swift forces the client to check if the value is `@uknown default` when switching over
597597
the enumeration to account for that. C++ follows a similar principle,
598-
by exposing an `unknown_default` case that can then be matched in a switch.
598+
by exposing an `unknownDefault` case that can then be matched in a switch.
599599

600600
For example, given the following resilient enumeration:
601601

@@ -620,14 +620,14 @@ void test(const DateFormatStyle &style) {
620620
case DateFormatStyle::full:
621621
...
622622
break;
623-
case DateFormatStyle::unknown_default: // just like Swift's @unknown default
623+
case DateFormatStyle::unknownDefault: // just like Swift's @unknown default
624624
// Some case value added in a future version of enum.
625625
break;
626626
}
627627
}
628628
```
629629
630-
The `unknown_default` case value is not a constructible case and you will get a compiler error if you try to construct it in C++.
630+
The `unknownDefault` case value is not a constructible case and you will get a compiler error if you try to construct it in C++.
631631
632632
## Using Swift Class Types
633633

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 106 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -414,107 +414,68 @@ class DeclAndTypePrinter::Implementation
414414
return p1.second.tag < p2.second.tag;
415415
});
416416

417-
if (elementTagMapping.empty()) {
418-
os << "\n";
419-
return;
420-
}
421-
422-
os << " enum class cases {\n";
423-
for (const auto &pair : elementTagMapping) {
424-
os << " ";
417+
os << '\n';
418+
os << " enum class cases {";
419+
llvm::interleave(elementTagMapping, os, [&](const auto &pair){
420+
os << "\n ";
425421
syntaxPrinter.printIdentifier(pair.first->getNameStr());
426-
os << ",\n";
427-
}
428-
os << " };\n"; // enum class cases' closing bracket
429-
430-
// Printing operator cases()
431-
os << " inline operator cases() const {\n";
432-
if (ED->isResilient()) {
433-
os << " auto tag = _getEnumTag();\n";
434-
for (const auto &pair : elementTagMapping) {
435-
os << " if (tag == " << cxx_synthesis::getCxxImplNamespaceName();
436-
os << "::" << pair.second.globalVariableName << ") return cases::";
437-
syntaxPrinter.printIdentifier(pair.first->getNameStr());
438-
os << ";\n";
439-
}
440-
// TODO: change to Swift's fatalError when it's available in C++
441-
os << " abort();\n";
442-
} else { // non-resilient enum
443-
os << " switch (_getEnumTag()) {\n";
444-
for (const auto &pair : elementTagMapping) {
445-
os << " case " << pair.second.tag << ": return cases::";
446-
syntaxPrinter.printIdentifier(pair.first->getNameStr());
447-
os << ";\n";
448-
}
449-
// TODO: change to Swift's fatalError when it's available in C++
450-
os << " default: abort();\n";
451-
os << " }\n"; // switch's closing bracket
452-
}
453-
os << " }\n"; // operator cases()'s closing bracket
454-
422+
}, ",");
423+
// TODO: allow custom name for this special case
424+
auto resilientUnknownDefaultCaseName = "unknownDefault";
455425
if (ED->isResilient()) {
456-
os << " inline bool inResilientUnknownCase() const {\n";
457-
os << " auto tag = _getEnumTag();\n";
458-
os << " return";
459-
llvm::interleave(
460-
elementTagMapping, os,
461-
[&](const auto &pair) {
462-
os << "\n tag != " << cxx_synthesis::getCxxImplNamespaceName()
463-
<< "::" << pair.second.globalVariableName;
464-
},
465-
" &&");
466-
os << ";\n";
467-
os << " }\n";
426+
os << ",\n " << resilientUnknownDefaultCaseName;
468427
}
469-
470-
// Printing case-related functions
428+
os << "\n };\n\n"; // enum class cases' closing bracket
429+
430+
// Printing struct, is, and get functions for each case
471431
DeclAndTypeClangFunctionPrinter clangFuncPrinter(
472432
os, owningPrinter.prologueOS, owningPrinter.typeMapping,
473433
owningPrinter.interopContext);
474-
475-
for (const auto &pair : elementTagMapping) {
434+
435+
auto printIsFunction = [&](StringRef caseName, EnumDecl *ED) {
476436
os << " inline bool is";
477-
auto name = pair.first->getNameStr().str();
437+
std::string name;
438+
llvm::raw_string_ostream nameStream(name);
439+
ClangSyntaxPrinter(nameStream).printIdentifier(caseName);
478440
name[0] = std::toupper(name[0]);
479441
os << name << "() const {\n";
480-
os << " return _getEnumTag() == ";
481-
if (ED->isResilient()) {
482-
os << cxx_synthesis::getCxxImplNamespaceName()
483-
<< "::" << pair.second.globalVariableName;
484-
} else {
485-
os << pair.second.tag;
486-
}
487-
os << ";\n }\n";
488-
489-
if (!pair.first->hasAssociatedValues()) {
490-
continue;
491-
}
492-
493-
auto associatedValueList = pair.first->getParameterList();
442+
os << " return *this == ";
443+
syntaxPrinter.printBaseName(ED);
444+
os << "::";
445+
syntaxPrinter.printIdentifier(caseName);
446+
os << ";\n";
447+
os << " }\n";
448+
};
449+
450+
auto printGetFunction = [&](EnumElementDecl *elementDecl) {
451+
auto associatedValueList = elementDecl->getParameterList();
494452
// TODO: add tuple type support
495453
if (associatedValueList->size() > 1) {
496-
continue;
454+
return;
497455
}
498456
auto firstType = associatedValueList->front()->getType();
499457
auto firstTypeDecl = firstType->getNominalOrBoundGenericNominal();
500458
OptionalTypeKind optKind;
501459
std::tie(firstType, optKind) =
502460
getObjectTypeAndOptionality(firstTypeDecl, firstType);
503-
504-
// FIXME: (tongjie) may have to forward declare return type
461+
462+
auto name = elementDecl->getNameStr().str();
463+
name[0] = std::toupper(name[0]);
464+
465+
// FIXME: may have to forward declare return type
505466
os << " inline ";
506467
clangFuncPrinter.printClangFunctionReturnType(
507468
firstType, optKind, firstTypeDecl->getModuleContext(),
508469
owningPrinter.outputLang);
509470
os << " get" << name << "() const {\n";
510471
os << " if (!is" << name << "()) abort();\n";
511472
os << " alignas(";
512-
syntaxPrinter.printBaseName(ED);
473+
syntaxPrinter.printBaseName(elementDecl->getParentEnum());
513474
os << ") unsigned char buffer[sizeof(";
514-
syntaxPrinter.printBaseName(ED);
475+
syntaxPrinter.printBaseName(elementDecl->getParentEnum());
515476
os << ")];\n";
516477
os << " auto *thisCopy = new(buffer) ";
517-
syntaxPrinter.printBaseName(ED);
478+
syntaxPrinter.printBaseName(elementDecl->getParentEnum());
518479
os << "(*this);\n";
519480
os << " char * _Nonnull payloadFromDestruction = "
520481
"thisCopy->_destructiveProjectEnumData();\n";
@@ -531,7 +492,7 @@ class DeclAndTypePrinter::Implementation
531492
} else {
532493
os << " return ";
533494
syntaxPrinter.printModuleNamespaceQualifiersIfNeeded(
534-
firstTypeDecl->getModuleContext(), ED->getModuleContext());
495+
firstTypeDecl->getModuleContext(), elementDecl->getParentEnum()->getModuleContext());
535496
os << cxx_synthesis::getCxxImplNamespaceName();
536497
os << "::";
537498
ClangValueTypePrinter::printCxxImplClassName(os, firstTypeDecl);
@@ -542,8 +503,77 @@ class DeclAndTypePrinter::Implementation
542503
os << "::initializeWithTake(result, payloadFromDestruction);\n";
543504
os << " });\n";
544505
}
545-
os << " }\n";
506+
os << " }\n"; // closing bracket of get function
507+
};
508+
509+
auto printStruct = [&](StringRef caseName, EnumElementDecl *elementDecl) {
510+
os << " static struct { // impl struct for case " << caseName << '\n';
511+
os << " inline constexpr operator cases() const {\n";
512+
os << " return cases::";
513+
syntaxPrinter.printIdentifier(caseName);
514+
os << ";\n";
515+
os << " }\n";
516+
if (elementDecl != nullptr) {
517+
os << " inline ";
518+
syntaxPrinter.printBaseName(elementDecl->getParentEnum());
519+
os << " operator()(";
520+
// TODO: implement parameter for associated value
521+
os << ") const {\n";
522+
// TODO: print _make for now; need to implement actual code making an enum
523+
os << " return ";
524+
syntaxPrinter.printBaseName(elementDecl->getParentEnum());
525+
os << "::_make();\n";
526+
os << " }\n";
527+
}
528+
os << " } ";
529+
syntaxPrinter.printIdentifier(caseName);
530+
os << ";\n";
531+
};
532+
533+
for (const auto &pair : elementTagMapping) {
534+
// Printing struct
535+
printStruct(pair.first->getNameStr(), pair.first);
536+
// Printing `is` function
537+
printIsFunction(pair.first->getNameStr(), ED);
538+
if (pair.first->hasAssociatedValues()) {
539+
// Printing `get` function
540+
printGetFunction(pair.first);
541+
}
542+
os << '\n';
543+
}
544+
545+
if (ED->isResilient()) {
546+
// Printing struct for unknownDefault
547+
printStruct(resilientUnknownDefaultCaseName, /* elementDecl */ nullptr);
548+
// Printing isUnknownDefault
549+
printIsFunction(resilientUnknownDefaultCaseName, ED);
550+
os << '\n';
551+
}
552+
os << '\n';
553+
554+
// Printing operator cases()
555+
os << " inline operator cases() const {\n";
556+
if (ED->isResilient()) {
557+
os << " auto tag = _getEnumTag();\n";
558+
for (const auto &pair : elementTagMapping) {
559+
os << " if (tag == " << cxx_synthesis::getCxxImplNamespaceName();
560+
os << "::" << pair.second.globalVariableName << ") return cases::";
561+
syntaxPrinter.printIdentifier(pair.first->getNameStr());
562+
os << ";\n";
563+
}
564+
os << " return cases::" << resilientUnknownDefaultCaseName << ";\n";
565+
} else { // non-resilient enum
566+
os << " switch (_getEnumTag()) {\n";
567+
for (const auto &pair : elementTagMapping) {
568+
os << " case " << pair.second.tag << ": return cases::";
569+
syntaxPrinter.printIdentifier(pair.first->getNameStr());
570+
os << ";\n";
571+
}
572+
// TODO: change to Swift's fatalError when it's available in C++
573+
os << " default: abort();\n";
574+
os << " }\n"; // switch's closing bracket
546575
}
576+
os << " }\n"; // operator cases()'s closing bracket
547577
os << "\n";
548578
});
549579
os << outOfLineDefinitions;

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,35 @@ void ClangValueTypePrinter::printValueTypeDecl(
254254
os << " friend class " << cxx_synthesis::getCxxImplNamespaceName() << "::";
255255
printCxxImplClassName(os, typeDecl);
256256
os << ";\n";
257-
os << "};\n\n";
257+
os << "};\n";
258+
// Print the definition of enum static struct data memebers
259+
if (isa<EnumDecl>(typeDecl)) {
260+
auto tagMapping = interopContext.getIrABIDetails().getEnumTagMapping(cast<EnumDecl>(typeDecl));
261+
for (const auto& pair : tagMapping) {
262+
os << "decltype(";
263+
printer.printBaseName(typeDecl);
264+
os << "::";
265+
printer.printIdentifier(pair.first->getNameStr());
266+
os << ") ";
267+
printer.printBaseName(typeDecl);
268+
os << "::";
269+
printer.printIdentifier(pair.first->getNameStr());
270+
os << ";\n";
271+
}
272+
if (isOpaqueLayout) {
273+
os << "decltype(";
274+
printer.printBaseName(typeDecl);
275+
// TODO: allow custom name for this special case
276+
os << "::";
277+
printer.printIdentifier("unknownDefault");
278+
os << ") ";
279+
printer.printBaseName(typeDecl);
280+
os << "::";
281+
printer.printIdentifier("unknownDefault");
282+
os << ";\n";
283+
}
284+
}
285+
os << '\n';
258286

259287
const auto *moduleContext = typeDecl->getModuleContext();
260288
// Print out the "hidden" _impl class.

test/Interop/SwiftToCxx/enums/resilient-enum-in-cxx-execution.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,38 @@
1818
#include <iostream>
1919
#include "enums.h"
2020

21+
using namespace Enums;
22+
23+
void useFooInSwitch(const Foo& f) {
24+
switch (f) {
25+
case Foo::a:
26+
std::cout << "Foo::a\n";
27+
break;;
28+
case Foo::unknownDefault:
29+
std::cout << "Foo::unknownDefault\n";
30+
break;
31+
}
32+
}
33+
2134
int main() {
22-
using namespace Enums;
2335
auto f1 = makeFoo(10);
2436
auto f2 = makeFoo(-10);
2537

2638
printFoo(f1);
2739
printFoo(f2);
2840

29-
assert(!f2.inResilientUnknownCase());
30-
if (f1.inResilientUnknownCase()) {
41+
assert(!f2.isUnknownDefault());
42+
if (f1.isUnknownDefault()) {
3143
std::cout << "f1.inResilientUnknownCase()\n";
3244
assert(!f1.isA());
3345
} else {
3446
assert(f1.isA());
3547
assert(f1.getA() == 10.0);
3648
}
3749

50+
useFooInSwitch(f1);
51+
useFooInSwitch(f2);
52+
3853
return 0;
3954
}
4055

@@ -43,3 +58,7 @@ int main() {
4358
// CHECK-NEXT: a(-10.0)
4459

4560
// NEW_CASE: f1.inResilientUnknownCase()
61+
62+
// NEW_CASE: Foo::unknownDefault
63+
// OLD_CASE: Foo::a
64+
// CHECK-NEXT: Foo::a

test/Interop/SwiftToCxx/enums/resilient-enum-in-cxx.swift

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,22 @@ public func printFoo(_ x: Foo) {
4242
// CHECK-EMPTY:
4343
// CHECK-NEXT: class Foo final {
4444
// CHECK-NEXT: public:
45+
// CHECK: enum class cases {
46+
// CHECK-NEXT: a,
47+
// NEW_CASE-NEXT: b,
48+
// CHECK-NEXT: unknownDefault
49+
// CHECK-NEXT: }
50+
// CHECK: static struct { // impl struct for case unknownDefault
51+
// CHECK-NEXT: constexpr operator cases() const {
52+
// CHECK-NEXT: return cases::unknownDefault;
53+
// CHECK-NEXT: }
54+
// CHECK-NEXT: } unknownDefault;
55+
// CHECK-NEXT: inline bool isUnknownDefault() const {
56+
// CHECK-NEXT: return *this == Foo::unknownDefault;
57+
// CHECK-NEXT: }
4558
// CHECK: inline operator cases() const {
4659
// CHECK-NEXT: auto tag = _getEnumTag();
4760
// CHECK-NEXT: if (tag == _impl::$s5Enums3FooO1ayACSdcACmFWC) return cases::a;
4861
// NEW_CASE-NEXT: if (tag == _impl::$s5Enums3FooO1byACSicACmFWC) return cases::b;
49-
// CHECK-NEXT: abort();
50-
// CHECK-NEXT: }
51-
// CHECK-NEXT: inline bool inResilientUnknownCase() const {
52-
// CHECK-NEXT: auto tag = _getEnumTag();
53-
// CHECK-NEXT: return
54-
// OLD_CASE-NEXT: tag != _impl::$s5Enums3FooO1ayACSdcACmFWC;
55-
// NEW_CASE-NEXT: tag != _impl::$s5Enums3FooO1ayACSdcACmFWC &&
56-
// NEW_CASE-NEXT: tag != _impl::$s5Enums3FooO1byACSicACmFWC;
57-
// CHECK-NEXT: }
58-
// CHECK-NEXT: inline bool isA() const {
59-
// CHECK-NEXT: return _getEnumTag() == _impl::$s5Enums3FooO1ayACSdcACmFWC;
62+
// CHECK-NEXT: return cases::unknownDefault;
6063
// CHECK-NEXT: }
61-
// NEW_CASE: inline bool isB() const {
62-
// NEW_CASE-NEXT: return _getEnumTag() == _impl::$s5Enums3FooO1byACSicACmFWC;
63-
// NEW_CASE-NEXT: }

0 commit comments

Comments
 (0)