Skip to content

Commit ff28b56

Browse files
committed
[Interop][SwiftToCxx] add support for resilient enum
1 parent b8c61e1 commit ff28b56

File tree

7 files changed

+207
-46
lines changed

7 files changed

+207
-46
lines changed

include/swift/IRGen/IRABIDetailsProvider.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,15 @@ class IRABIDetailsProvider {
105105
/// access function.
106106
FunctionABISignature getTypeMetadataAccessFunctionSignature();
107107

108+
struct EnumElementInfo {
109+
unsigned tag;
110+
StringRef globalVariableName;
111+
};
112+
108113
/// Returns EnumElementDecls (enum cases) in their declaration order with
109114
/// their tag indices from the given EnumDecl
110-
llvm::MapVector<EnumElementDecl *, unsigned> getEnumTagMapping(EnumDecl *ED);
115+
llvm::MapVector<EnumElementDecl *, EnumElementInfo>
116+
getEnumTagMapping(const EnumDecl *ED);
111117

112118
/// Returns the additional params if they exist after lowering the function.
113119
SmallVector<ABIAdditionalParam, 1>

lib/IRGen/IRABIDetailsProvider.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,18 @@ class IRABIDetailsProviderImpl {
126126
return {returnTy, {paramTy}};
127127
}
128128

129-
llvm::MapVector<EnumElementDecl *, unsigned> getEnumTagMapping(EnumDecl *ED) {
130-
llvm::MapVector<EnumElementDecl *, unsigned> elements;
129+
llvm::MapVector<EnumElementDecl *, IRABIDetailsProvider::EnumElementInfo>
130+
getEnumTagMapping(const EnumDecl *ED) {
131+
llvm::MapVector<EnumElementDecl *, IRABIDetailsProvider::EnumElementInfo>
132+
elements;
131133
auto &enumImplStrat =
132134
getEnumImplStrategy(IGM, ED->getDeclaredType()->getCanonicalType());
133135

134136
for (auto *element : ED->getAllElements()) {
135137
auto tagIdx = enumImplStrat.getTagIndex(element);
136-
elements.insert({element, tagIdx});
138+
auto *global = cast<llvm::GlobalVariable>(
139+
IGM.getAddrOfEnumCase(element, NotForDefinition).getAddress());
140+
elements.insert({element, {tagIdx, global->getName()}});
137141
}
138142

139143
return elements;
@@ -217,7 +221,7 @@ IRABIDetailsProvider::getTypeMetadataAccessFunctionSignature() {
217221
return impl->getTypeMetadataAccessFunctionSignature();
218222
}
219223

220-
llvm::MapVector<EnumElementDecl *, unsigned>
221-
IRABIDetailsProvider::getEnumTagMapping(EnumDecl *ED) {
224+
llvm::MapVector<EnumElementDecl *, IRABIDetailsProvider::EnumElementInfo>
225+
IRABIDetailsProvider::getEnumTagMapping(const EnumDecl *ED) {
222226
return impl->getEnumTagMapping(ED);
223227
}

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ class DeclAndTypePrinter::Implementation
411411
owningPrinter.interopContext.getIrABIDetails().getEnumTagMapping(ED);
412412
// Sort cases based on their assigned tag indices
413413
llvm::stable_sort(elementTagMapping, [](const auto &p1, const auto &p2) {
414-
return p1.second < p2.second;
414+
return p1.second.tag < p2.second.tag;
415415
});
416416

417417
if (elementTagMapping.empty()) {
@@ -429,18 +429,44 @@ class DeclAndTypePrinter::Implementation
429429

430430
// Printing operator cases()
431431
os << " inline operator cases() const {\n";
432-
os << " switch (_getEnumTag()) {\n";
433-
for (const auto &pair : elementTagMapping) {
434-
// TODO: have to use global variable for resilient enum
435-
os << " case " << pair.second << ": return cases::";
436-
syntaxPrinter.printIdentifier(pair.first->getNameStr());
437-
os << ";\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
438452
}
439-
// TODO: change to Swift's fatalError when it's available in C++
440-
os << " default: abort();\n";
441-
os << " }\n"; // switch's closing bracket
442453
os << " }\n"; // operator cases()'s closing bracket
443454

455+
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";
468+
}
469+
444470
// Printing case-related functions
445471
DeclAndTypeClangFunctionPrinter clangFuncPrinter(
446472
os, owningPrinter.prologueOS, owningPrinter.typeMapping,
@@ -451,8 +477,13 @@ class DeclAndTypePrinter::Implementation
451477
auto name = pair.first->getNameStr().str();
452478
name[0] = std::toupper(name[0]);
453479
os << name << "() const {\n";
454-
os << " return *this == cases::";
455-
syntaxPrinter.printIdentifier(pair.first->getNameStr());
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+
}
456487
os << ";\n }\n";
457488

458489
if (!pair.first->hasAssociatedValues()) {

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,22 @@ void ClangValueTypePrinter::printValueTypeDecl(
113113
// access type metadata.
114114
printer.printCTypeMetadataTypeFunction(
115115
typeDecl, typeMetadataFuncName);
116+
// Print out global variables for resilient enum
117+
// cases
118+
if (isa<EnumDecl>(typeDecl) && isOpaqueLayout) {
119+
auto elementTagMapping =
120+
interopContext.getIrABIDetails()
121+
.getEnumTagMapping(
122+
cast<EnumDecl>(typeDecl));
123+
os << "// Tags for resilient enum ";
124+
os << typeDecl->getName().str() << '\n';
125+
os << "extern \"C\" {\n";
126+
for (const auto &pair : elementTagMapping) {
127+
os << "extern int "
128+
<< pair.second.globalVariableName << ";\n";
129+
}
130+
os << "}\n";
131+
}
116132
});
117133

118134
auto printEnumVWTableVariable = [&](StringRef metadataName = "metadata",
@@ -265,17 +281,13 @@ void ClangValueTypePrinter::printValueTypeDecl(
265281
os << " return result;\n";
266282
os << " }\n";
267283
// Print out helper function for initializeWithTake
268-
// TODO: (tongjie) support opaque layout
269-
if (!isOpaqueLayout) {
270-
os << " static inline void initializeWithTake(char * _Nonnull "
271-
"destStorage, char * _Nonnull srcStorage) {\n";
272-
ClangValueTypePrinter::printValueWitnessTableAccessAsVariable(
273-
os, typeMetadataFuncName);
274-
os << " vwTable->initializeWithTake(destStorage, srcStorage, "
275-
"metadata._0);\n";
276-
os << " }\n";
277-
}
278-
284+
os << " static inline void initializeWithTake(char * _Nonnull "
285+
"destStorage, char * _Nonnull srcStorage) {\n";
286+
ClangValueTypePrinter::printValueWitnessTableAccessAsVariable(
287+
os, typeMetadataFuncName);
288+
os << " vwTable->initializeWithTake(destStorage, srcStorage, "
289+
"metadata._0);\n";
290+
os << " }\n";
279291
os << "};\n";
280292
});
281293

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend %S/resilient-enum-in-cxx.swift -enable-library-evolution -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+
7+
// RUN: %target-interop-build-swift %S/resilient-enum-in-cxx.swift -enable-library-evolution -o %t/swift-enums-execution -Xlinker %t/swift-enums-execution.o -module-name Enums -Xfrontend -entry-point-function-name -Xfrontend swiftMain
8+
// RUN: %target-codesign %t/swift-enums-execution
9+
// RUN: %target-run %t/swift-enums-execution | %FileCheck --check-prefixes=CHECK,OLD_CASE %s
10+
11+
// RUN: %target-interop-build-swift %S/resilient-enum-in-cxx.swift -enable-library-evolution -o %t//swift-enums-execution-new -Xlinker %t/swift-enums-execution.o -module-name Enums -Xfrontend -entry-point-function-name -Xfrontend swiftMain -D NEW_CASE
12+
// RUN: %target-codesign %t/swift-enums-execution-new
13+
// RUN: %target-run %t/swift-enums-execution-new | %FileCheck --check-prefixes=CHECK,NEW_CASE %s
14+
15+
// REQUIRES: executable_test
16+
17+
#include <cassert>
18+
#include <iostream>
19+
#include "enums.h"
20+
21+
int main() {
22+
using namespace Enums;
23+
auto f1 = makeFoo(10);
24+
auto f2 = makeFoo(-10);
25+
26+
printFoo(f1);
27+
printFoo(f2);
28+
29+
assert(!f2.inResilientUnknownCase());
30+
if (f1.inResilientUnknownCase()) {
31+
std::cout << "f1.inResilientUnknownCase()\n";
32+
assert(!f1.isA());
33+
} else {
34+
assert(f1.isA());
35+
assert(f1.getA() == 10.0);
36+
}
37+
38+
return 0;
39+
}
40+
41+
// OLD_CASE: a(10.0)
42+
// NEW_CASE: b(10)
43+
// CHECK-NEXT: a(-10.0)
44+
45+
// NEW_CASE: f1.inResilientUnknownCase()
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend %s -enable-library-evolution -typecheck -module-name Enums -clang-header-expose-public-decls -emit-clang-header-path %t/enums.h
4+
// RUN: %FileCheck --check-prefixes=CHECK,OLD_CASE %s < %t/enums.h
5+
6+
// RUN: %target-swift-frontend %s -enable-library-evolution -D NEW_CASE -typecheck -module-name Enums -clang-header-expose-public-decls -emit-clang-header-path %t/enums_new_case.h
7+
// RUN: %FileCheck --check-prefixes=CHECK,NEW_CASE %s < %t/enums_new_case.h
8+
9+
// RUN: %check-interop-cxx-header-in-clang(%t/enums.h -Wno-unused-private-field -Wno-unused-function)
10+
// RUN: %check-interop-cxx-header-in-clang(%t/enums_new_case.h -Wno-unused-private-field -Wno-unused-function)
11+
12+
public enum Foo {
13+
case a(Double)
14+
#if NEW_CASE
15+
case b(Int)
16+
#endif
17+
}
18+
19+
public func makeFoo(_ x: Int) -> Foo {
20+
#if NEW_CASE
21+
if x >= 0 {
22+
return .b(x)
23+
} else {
24+
return .a(Double(x))
25+
}
26+
#else
27+
return .a(Double(x))
28+
#endif
29+
}
30+
31+
public func printFoo(_ x: Foo) {
32+
print(x)
33+
}
34+
35+
// CHECK: // Tags for resilient enum Foo
36+
// CHECK-NEXT: extern "C" {
37+
// CHECK-NEXT: extern int $s5Enums3FooO1ayACSdcACmFWC;
38+
// NEW_CASE-NEXT: extern int $s5Enums3FooO1byACSicACmFWC;
39+
// CHECK-NEXT: }
40+
// CHECK-EMPTY:
41+
// CHECK-NEXT: } // namespace _impl
42+
// CHECK-EMPTY:
43+
// CHECK-NEXT: class Foo final {
44+
// CHECK-NEXT: public:
45+
// CHECK: inline operator cases() const {
46+
// CHECK-NEXT: auto tag = _getEnumTag();
47+
// CHECK-NEXT: if (tag == _impl::$s5Enums3FooO1ayACSdcACmFWC) return cases::a;
48+
// 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;
60+
// CHECK-NEXT: }
61+
// NEW_CASE: inline bool isB() const {
62+
// NEW_CASE-NEXT: return _getEnumTag() == _impl::$s5Enums3FooO1byACSicACmFWC;
63+
// NEW_CASE-NEXT: }

test/Interop/SwiftToCxx/enums/swift-enum-case-functions.swift

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,13 @@ public func checkIntDoubleOrBignum(_ x: IntDoubleOrBignum, tag: Int) -> Bool {
162162
// CHECK-NEXT: }
163163
// CHECK-NEXT: }
164164
// CHECK-NEXT: inline bool isSecond() const {
165-
// CHECK-NEXT: return *this == cases::second;
165+
// CHECK-NEXT: return _getEnumTag() == 0;
166166
// CHECK-NEXT: }
167167
// CHECK: inline bool isFirst() const {
168-
// CHECK-NEXT: return *this == cases::first;
168+
// CHECK-NEXT: return _getEnumTag() == 1;
169169
// CHECK-NEXT: }
170170
// CHECK-NEXT: inline bool isThird() const {
171-
// CHECK-NEXT: return *this == cases::third;
171+
// CHECK-NEXT: return _getEnumTag() == 2;
172172
// CHECK-NEXT: }
173173
// CHECK: inline int _getEnumTag() const {
174174
// CHECK-NEXT: auto metadata = _impl::$s5Enums12BoolWithCaseOMa(0);
@@ -193,13 +193,13 @@ public func checkIntDoubleOrBignum(_ x: IntDoubleOrBignum, tag: Int) -> Bool {
193193
// CHECK-NEXT: }
194194
// CHECK-NEXT: }
195195
// CHECK-NEXT: inline bool isOne() const {
196-
// CHECK-NEXT: return *this == cases::one;
196+
// CHECK-NEXT: return _getEnumTag() == 0;
197197
// CHECK-NEXT: }
198198
// CHECK: inline bool isTwo() const {
199-
// CHECK-NEXT: return *this == cases::two;
199+
// CHECK-NEXT: return _getEnumTag() == 1;
200200
// CHECK-NEXT: }
201201
// CHECK: inline bool isThree() const {
202-
// CHECK-NEXT: return *this == cases::three;
202+
// CHECK-NEXT: return _getEnumTag() == 2;
203203
// CHECK-NEXT: }
204204
// CHECK: inline int _getEnumTag() const {
205205
// CHECK-NEXT: auto metadata = _impl::$s5Enums9CLikeEnumOMa(0);
@@ -222,7 +222,7 @@ public func checkIntDoubleOrBignum(_ x: IntDoubleOrBignum, tag: Int) -> Bool {
222222
// CHECK-NEXT: }
223223
// CHECK-NEXT: }
224224
// CHECK-NEXT: inline bool isOne() const {
225-
// CHECK-NEXT: return *this == cases::one;
225+
// CHECK-NEXT: return _getEnumTag() == 0;
226226
// CHECK-NEXT: }
227227
// CHECK: inline int _getEnumTag() const {
228228
// CHECK-NEXT: auto metadata = _impl::$s5Enums8DataCaseOMa(0);
@@ -247,13 +247,13 @@ public func checkIntDoubleOrBignum(_ x: IntDoubleOrBignum, tag: Int) -> Bool {
247247
// CHECK-NEXT: }
248248
// CHECK-NEXT: }
249249
// CHECK-NEXT: inline bool isInt() const {
250-
// CHECK-NEXT: return *this == cases::Int;
250+
// CHECK-NEXT: return _getEnumTag() == 0;
251251
// CHECK-NEXT: }
252252
// CHECK: inline bool isDouble() const {
253-
// CHECK-NEXT: return *this == cases::Double;
253+
// CHECK-NEXT: return _getEnumTag() == 1;
254254
// CHECK-NEXT: }
255255
// CHECK: inline bool isBignum() const {
256-
// CHECK-NEXT: return *this == cases::Bignum;
256+
// CHECK-NEXT: return _getEnumTag() == 2;
257257
// CHECK-NEXT: }
258258
// CHECK: inline int _getEnumTag() const {
259259
// CHECK-NEXT: auto metadata = _impl::$s5Enums17IntDoubleOrBignumOMa(0);
@@ -278,13 +278,13 @@ public func checkIntDoubleOrBignum(_ x: IntDoubleOrBignum, tag: Int) -> Bool {
278278
// CHECK-NEXT: }
279279
// CHECK-NEXT: }
280280
// CHECK-NEXT: inline bool isInt() const {
281-
// CHECK-NEXT: return *this == cases::Int;
281+
// CHECK-NEXT: return _getEnumTag() == 0;
282282
// CHECK-NEXT: }
283283
// CHECK: inline bool isNegInfinity() const {
284-
// CHECK-NEXT: return *this == cases::NegInfinity;
284+
// CHECK-NEXT: return _getEnumTag() == 1;
285285
// CHECK-NEXT: }
286286
// CHECK: inline bool isPosInfinity() const {
287-
// CHECK-NEXT: return *this == cases::PosInfinity;
287+
// CHECK-NEXT: return _getEnumTag() == 2;
288288
// CHECK-NEXT: }
289289
// CHECK: inline int _getEnumTag() const {
290290
// CHECK-NEXT: auto metadata = _impl::$s5Enums13IntOrInfinityOMa(0);
@@ -310,16 +310,16 @@ public func checkIntDoubleOrBignum(_ x: IntDoubleOrBignum, tag: Int) -> Bool {
310310
// CHECK-NEXT: }
311311
// CHECK-NEXT: }
312312
// CHECK-NEXT: inline bool isSecond() const {
313-
// CHECK-NEXT: return *this == cases::second;
313+
// CHECK-NEXT: return _getEnumTag() == 0;
314314
// CHECK-NEXT: }
315315
// CHECK: inline bool isThird() const {
316-
// CHECK-NEXT: return *this == cases::third;
316+
// CHECK-NEXT: return _getEnumTag() == 1;
317317
// CHECK-NEXT: }
318318
// CHECK: inline bool isFirst() const {
319-
// CHECK-NEXT: return *this == cases::first;
319+
// CHECK-NEXT: return _getEnumTag() == 2;
320320
// CHECK-NEXT: }
321321
// CHECK-NEXT: inline bool isFourth() const {
322-
// CHECK-NEXT: return *this == cases::fourth;
322+
// CHECK-NEXT: return _getEnumTag() == 3;
323323
// CHECK-NEXT: }
324324
// CHECK: inline int _getEnumTag() const {
325325
// CHECK-NEXT: auto metadata = _impl::$s5Enums20MultipleBoolWithCaseOMa(0);

0 commit comments

Comments
 (0)