Skip to content

Commit 979bbf5

Browse files
committed
[Interop][SwiftToCxx] add support for resilient enum
1 parent 6c7e8c4 commit 979bbf5

File tree

6 files changed

+165
-27
lines changed

6 files changed

+165
-27
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: 35 additions & 10 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,43 @@ 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+
os << " abort();\n";
441+
} else { // non-resilient enum
442+
os << " switch (_getEnumTag()) {\n";
443+
for (const auto &pair : elementTagMapping) {
444+
os << " case " << pair.second.tag << ": return cases::";
445+
syntaxPrinter.printIdentifier(pair.first->getNameStr());
446+
os << ";\n";
447+
}
448+
// TODO: change to Swift's fatalError when it's available in C++
449+
os << " default: abort();\n";
450+
os << " }\n"; // switch's closing bracket
438451
}
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
442452
os << " }\n"; // operator cases()'s closing bracket
443453

454+
if (ED->isResilient()) {
455+
os << " inline bool inResilientUnknownCase() const {\n";
456+
os << " auto tag = _getEnumTag();\n";
457+
os << " return";
458+
llvm::interleave(
459+
elementTagMapping, os,
460+
[&](const auto &pair) {
461+
os << "\n tag !=" << cxx_synthesis::getCxxImplNamespaceName()
462+
<< "::" << pair.second.globalVariableName;
463+
},
464+
" &&");
465+
os << ";\n";
466+
os << " }\n";
467+
}
468+
444469
// Printing case-related functions
445470
DeclAndTypeClangFunctionPrinter clangFuncPrinter(
446471
os, owningPrinter.prologueOS, owningPrinter.typeMapping,

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,22 @@ void ClangValueTypePrinter::printValueTypeDecl(
126126
// access type metadata.
127127
printCTypeMetadataTypeFunction(os, typeDecl,
128128
typeMetadataFuncName);
129+
// Print out global variables for resilient enum
130+
// cases
131+
if (isa<EnumDecl>(typeDecl) && isOpaqueLayout) {
132+
auto elementTagMapping =
133+
interopContext.getIrABIDetails()
134+
.getEnumTagMapping(
135+
cast<EnumDecl>(typeDecl));
136+
os << "// Tags for resilient enum ";
137+
os << typeDecl->getName().str() << '\n';
138+
os << "extern \"C\" {\n";
139+
for (const auto &pair : elementTagMapping) {
140+
os << "extern int "
141+
<< pair.second.globalVariableName << ";\n";
142+
}
143+
os << "}\n";
144+
}
129145
});
130146

131147
auto printEnumVWTableVariable = [&](StringRef metadataName = "metadata",
@@ -278,17 +294,13 @@ void ClangValueTypePrinter::printValueTypeDecl(
278294
os << " return result;\n";
279295
os << " }\n";
280296
// Print out helper function for initializeWithTake
281-
// TODO: (tongjie) support opaque layout
282-
if (!isOpaqueLayout) {
283-
os << " static inline void initializeWithTake(char * _Nonnull "
284-
"destStorage, char * _Nonnull srcStorage) {\n";
285-
ClangValueTypePrinter::printValueWitnessTableAccessAsVariable(
286-
os, typeMetadataFuncName);
287-
os << " vwTable->initializeWithTake(destStorage, srcStorage, "
288-
"metadata._0);\n";
289-
os << " }\n";
290-
}
291-
297+
os << " static inline void initializeWithTake(char * _Nonnull "
298+
"destStorage, char * _Nonnull srcStorage) {\n";
299+
ClangValueTypePrinter::printValueWitnessTableAccessAsVariable(
300+
os, typeMetadataFuncName);
301+
os << " vwTable->initializeWithTake(destStorage, srcStorage, "
302+
"metadata._0);\n";
303+
os << " }\n";
292304
os << "};\n";
293305
});
294306

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
// 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
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+
int main() {
17+
using namespace Enums;
18+
19+
{
20+
auto f = makeFoo(0);
21+
assert(f == Foo::cases::a);
22+
}
23+
{
24+
auto f = makeFoo(1);
25+
assert(f == Foo::cases::b);
26+
assert(f.getB() == 123);
27+
}
28+
{
29+
auto f = makeFoo(2);
30+
assert(f == Foo::cases::c);
31+
}
32+
{
33+
auto f = makeFoo(3);
34+
assert(f == Foo::cases::d);
35+
assert(f.getD() == 2.34);
36+
}
37+
38+
return 0;
39+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -enable-library-evolution -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 Foo {
8+
case a, b(Int), c
9+
case d(Double)
10+
}
11+
12+
public func makeFoo(_ x: Int) -> Foo {
13+
switch x {
14+
case 0:
15+
return .a
16+
case 1:
17+
return .b(123)
18+
case 2:
19+
return .c
20+
default:
21+
return .d(2.34)
22+
}
23+
}
24+
25+
// CHECK: // Tags for resilient enum Foo
26+
// CHECK-NEXT: extern "C" {
27+
// CHECK-NEXT: extern int $s5Enums3FooO1ayA2CmFWC;
28+
// CHECK-NEXT: extern int $s5Enums3FooO1byACSicACmFWC;
29+
// CHECK-NEXT: extern int $s5Enums3FooO1cyA2CmFWC;
30+
// CHECK-NEXT: extern int $s5Enums3FooO1dyACSdcACmFWC;
31+
// CHECK-NEXT: }
32+
// CHECK-EMPTY:
33+
// CHECK-NEXT: } // namespace _impl
34+
// CHECK-EMPTY:
35+
// CHECK-NEXT: class Foo final {
36+
// CHECK-NEXT: public:
37+
// CHECK: inline operator cases() const {
38+
// CHECK-NEXT: auto tag = _getEnumTag();
39+
// CHECK-NEXT: if (tag == _impl::$s5Enums3FooO1byACSicACmFWC) return cases::b;
40+
// CHECK-NEXT: if (tag == _impl::$s5Enums3FooO1dyACSdcACmFWC) return cases::d;
41+
// CHECK-NEXT: if (tag == _impl::$s5Enums3FooO1ayA2CmFWC) return cases::a;
42+
// CHECK-NEXT: if (tag == _impl::$s5Enums3FooO1cyA2CmFWC) return cases::c;
43+
// CHECK-NEXT: abort();
44+
// CHECK-NEXT: }
45+
// CHECK-NEXT: inline bool inResilientUnknownCase() const {
46+
// CHECK-NEXT: auto tag = _getEnumTag();
47+
// CHECK-NEXT: return
48+
// CHECK-NEXT: tag !=_impl::$s5Enums3FooO1byACSicACmFWC &&
49+
// CHECK-NEXT: tag !=_impl::$s5Enums3FooO1dyACSdcACmFWC &&
50+
// CHECK-NEXT: tag !=_impl::$s5Enums3FooO1ayA2CmFWC &&
51+
// CHECK-NEXT: tag !=_impl::$s5Enums3FooO1cyA2CmFWC;
52+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)