Skip to content

Commit f5787c9

Browse files
authored
Merge pull request #60564 from WANGJIEKE/cxx-interop-new-enum-design
[Interop][SwiftToCxx] Update current enum implementation for new enum design
2 parents d40301f + 7115e64 commit f5787c9

File tree

6 files changed

+489
-155
lines changed

6 files changed

+489
-155
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: 107 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -414,107 +414,71 @@ 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 << " ";
425-
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-
417+
os << '\n';
418+
os << " enum class cases {";
419+
llvm::interleave(
420+
elementTagMapping, os,
421+
[&](const auto &pair) {
422+
os << "\n ";
423+
syntaxPrinter.printIdentifier(pair.first->getNameStr());
424+
},
425+
",");
426+
// TODO: allow custom name for this special case
427+
auto resilientUnknownDefaultCaseName = "unknownDefault";
455428
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";
429+
os << ",\n " << resilientUnknownDefaultCaseName;
468430
}
431+
os << "\n };\n\n"; // enum class cases' closing bracket
469432

470-
// Printing case-related functions
433+
// Printing struct, is, and get functions for each case
471434
DeclAndTypeClangFunctionPrinter clangFuncPrinter(
472435
os, owningPrinter.prologueOS, owningPrinter.typeMapping,
473436
owningPrinter.interopContext);
474437

475-
for (const auto &pair : elementTagMapping) {
438+
auto printIsFunction = [&](StringRef caseName, EnumDecl *ED) {
476439
os << " inline bool is";
477-
auto name = pair.first->getNameStr().str();
440+
std::string name;
441+
llvm::raw_string_ostream nameStream(name);
442+
ClangSyntaxPrinter(nameStream).printIdentifier(caseName);
478443
name[0] = std::toupper(name[0]);
479444
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-
}
445+
os << " return *this == ";
446+
syntaxPrinter.printBaseName(ED);
447+
os << "::";
448+
syntaxPrinter.printIdentifier(caseName);
449+
os << ";\n";
450+
os << " }\n";
451+
};
492452

493-
auto associatedValueList = pair.first->getParameterList();
453+
auto printGetFunction = [&](EnumElementDecl *elementDecl) {
454+
auto associatedValueList = elementDecl->getParameterList();
494455
// TODO: add tuple type support
495456
if (associatedValueList->size() > 1) {
496-
continue;
457+
return;
497458
}
498459
auto firstType = associatedValueList->front()->getType();
499460
auto firstTypeDecl = firstType->getNominalOrBoundGenericNominal();
500461
OptionalTypeKind optKind;
501462
std::tie(firstType, optKind) =
502463
getObjectTypeAndOptionality(firstTypeDecl, firstType);
503464

504-
// FIXME: (tongjie) may have to forward declare return type
465+
auto name = elementDecl->getNameStr().str();
466+
name[0] = std::toupper(name[0]);
467+
468+
// FIXME: may have to forward declare return type
505469
os << " inline ";
506470
clangFuncPrinter.printClangFunctionReturnType(
507471
firstType, optKind, firstTypeDecl->getModuleContext(),
508472
owningPrinter.outputLang);
509473
os << " get" << name << "() const {\n";
510474
os << " if (!is" << name << "()) abort();\n";
511475
os << " alignas(";
512-
syntaxPrinter.printBaseName(ED);
476+
syntaxPrinter.printBaseName(elementDecl->getParentEnum());
513477
os << ") unsigned char buffer[sizeof(";
514-
syntaxPrinter.printBaseName(ED);
478+
syntaxPrinter.printBaseName(elementDecl->getParentEnum());
515479
os << ")];\n";
516480
os << " auto *thisCopy = new(buffer) ";
517-
syntaxPrinter.printBaseName(ED);
481+
syntaxPrinter.printBaseName(elementDecl->getParentEnum());
518482
os << "(*this);\n";
519483
os << " char * _Nonnull payloadFromDestruction = "
520484
"thisCopy->_destructiveProjectEnumData();\n";
@@ -531,7 +495,8 @@ class DeclAndTypePrinter::Implementation
531495
} else {
532496
os << " return ";
533497
syntaxPrinter.printModuleNamespaceQualifiersIfNeeded(
534-
firstTypeDecl->getModuleContext(), ED->getModuleContext());
498+
firstTypeDecl->getModuleContext(),
499+
elementDecl->getParentEnum()->getModuleContext());
535500
os << cxx_synthesis::getCxxImplNamespaceName();
536501
os << "::";
537502
ClangValueTypePrinter::printCxxImplClassName(os, firstTypeDecl);
@@ -542,8 +507,77 @@ class DeclAndTypePrinter::Implementation
542507
os << "::initializeWithTake(result, payloadFromDestruction);\n";
543508
os << " });\n";
544509
}
545-
os << " }\n";
510+
os << " }\n"; // closing bracket of get function
511+
};
512+
513+
auto printStruct = [&](StringRef caseName, EnumElementDecl *elementDecl) {
514+
os << " static struct { // impl struct for case " << caseName << '\n';
515+
os << " inline constexpr operator cases() const {\n";
516+
os << " return cases::";
517+
syntaxPrinter.printIdentifier(caseName);
518+
os << ";\n";
519+
os << " }\n";
520+
if (elementDecl != nullptr) {
521+
os << " inline ";
522+
syntaxPrinter.printBaseName(elementDecl->getParentEnum());
523+
os << " operator()(";
524+
// TODO: implement parameter for associated value
525+
os << ") const {\n";
526+
// TODO: print _make for now; need to print actual code making an enum
527+
os << " return ";
528+
syntaxPrinter.printBaseName(elementDecl->getParentEnum());
529+
os << "::_make();\n";
530+
os << " }\n";
531+
}
532+
os << " } ";
533+
syntaxPrinter.printIdentifier(caseName);
534+
os << ";\n";
535+
};
536+
537+
for (const auto &pair : elementTagMapping) {
538+
// Printing struct
539+
printStruct(pair.first->getNameStr(), pair.first);
540+
// Printing `is` function
541+
printIsFunction(pair.first->getNameStr(), ED);
542+
if (pair.first->hasAssociatedValues()) {
543+
// Printing `get` function
544+
printGetFunction(pair.first);
545+
}
546+
os << '\n';
547+
}
548+
549+
if (ED->isResilient()) {
550+
// Printing struct for unknownDefault
551+
printStruct(resilientUnknownDefaultCaseName, /* elementDecl */ nullptr);
552+
// Printing isUnknownDefault
553+
printIsFunction(resilientUnknownDefaultCaseName, ED);
554+
os << '\n';
555+
}
556+
os << '\n';
557+
558+
// Printing operator cases()
559+
os << " inline operator cases() const {\n";
560+
if (ED->isResilient()) {
561+
os << " auto tag = _getEnumTag();\n";
562+
for (const auto &pair : elementTagMapping) {
563+
os << " if (tag == " << cxx_synthesis::getCxxImplNamespaceName();
564+
os << "::" << pair.second.globalVariableName << ") return cases::";
565+
syntaxPrinter.printIdentifier(pair.first->getNameStr());
566+
os << ";\n";
567+
}
568+
os << " return cases::" << resilientUnknownDefaultCaseName << ";\n";
569+
} else { // non-resilient enum
570+
os << " switch (_getEnumTag()) {\n";
571+
for (const auto &pair : elementTagMapping) {
572+
os << " case " << pair.second.tag << ": return cases::";
573+
syntaxPrinter.printIdentifier(pair.first->getNameStr());
574+
os << ";\n";
575+
}
576+
// TODO: change to Swift's fatalError when it's available in C++
577+
os << " default: abort();\n";
578+
os << " }\n"; // switch's closing bracket
546579
}
580+
os << " }\n"; // operator cases()'s closing bracket
547581
os << "\n";
548582
});
549583
os << outOfLineDefinitions;

lib/PrintAsClang/PrintClangValueType.cpp

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

262291
const auto *moduleContext = typeDecl->getModuleContext();
263292
// 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)