Skip to content

Commit 342126e

Browse files
authored
Merge pull request #61626 from Robertorosmaninho/cxx-interop/DynamicCastToSwiftError
[Interop] [SwiftToCxx] Introduce Dynamic Cast to swift::Error
2 parents 8a05768 + 5102d19 commit 342126e

File tree

4 files changed

+87
-7
lines changed

4 files changed

+87
-7
lines changed

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,8 @@ class ModuleWriter {
561561

562562
SmallVector<ProtocolConformance *, 1> conformances;
563563
auto errorTypeProto = ctx.getProtocol(KnownProtocolKind::Error);
564-
if (ED->lookupConformance(errorTypeProto, conformances)) {
564+
if (outputLangMode != OutputLanguageMode::Cxx
565+
&& ED->lookupConformance(errorTypeProto, conformances)) {
565566
bool hasDomainCase = std::any_of(ED->getAllElements().begin(),
566567
ED->getAllElements().end(),
567568
[](const EnumElementDecl *elem) {

lib/PrintAsClang/_SwiftCxxInteroperability.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
#if defined(_WIN32)
2525
#include <malloc.h>
2626
#endif
27+
#if !defined(SWIFT_CALL)
28+
# define SWIFT_CALL __attribute__((swiftcall))
29+
#endif
2730

2831
// FIXME: Use always_inline, artificial.
2932
#define SWIFT_INLINE_THUNK inline
@@ -191,6 +194,46 @@ extern "C" void *_Nonnull swift_errorRetain(void *_Nonnull swiftError) noexcept;
191194

192195
extern "C" void swift_errorRelease(void *_Nonnull swiftError) noexcept;
193196

197+
extern "C" int $ss5ErrorMp; // external global %swift.protocol, align 4
198+
199+
extern "C"
200+
const void * _Nullable
201+
swift_getTypeByMangledNameInContext(
202+
const char *_Nullable typeNameStart,
203+
size_t typeNameLength,
204+
const void *_Nullable context,
205+
const void *_Nullable const *_Nullable genericArgs) SWIFT_CALL;
206+
207+
extern "C" bool swift_dynamicCast(void *_Nullable dest, void *_Nullable src,
208+
const void *_Nullable srcType,
209+
const void * _Nullable targetType,
210+
uint32_t flags);
211+
212+
struct SymbolicP {
213+
alignas(2) uint8_t _1;
214+
uint32_t _2;
215+
uint8_t _3[2];
216+
uint8_t _4;
217+
} __attribute__((packed));
218+
219+
inline const void *_Nullable getErrorMetadata() {
220+
static swift::SymbolicP errorSymbol;
221+
static int *_Nonnull got_ss5ErrorMp = &$ss5ErrorMp;
222+
errorSymbol._1 = 2;
223+
errorSymbol._2 = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&got_ss5ErrorMp) - reinterpret_cast<uintptr_t>(&errorSymbol._2));
224+
errorSymbol._3[0] = '_';
225+
errorSymbol._3[1] = 'p';
226+
errorSymbol._4 = 0;
227+
static_assert(sizeof(errorSymbol) == 8, "");
228+
auto charErrorSymbol = reinterpret_cast<const char *>(&errorSymbol);
229+
230+
const void *ptr2 =
231+
swift::swift_getTypeByMangledNameInContext(charErrorSymbol,
232+
sizeof(errorSymbol) - 1,
233+
nullptr, nullptr);
234+
return ptr2;
235+
}
236+
194237
class Error {
195238
public:
196239
Error() {}
@@ -209,6 +252,29 @@ class Error {
209252
opaqueValue = other.opaqueValue;
210253
}
211254

255+
// FIXME: Return a Swift::Optional instead.
256+
template<class T>
257+
T as() {
258+
alignas(alignof(T)) char buffer[sizeof(T)];
259+
const void *em = getErrorMetadata();
260+
void *ep = getPointerToOpaquePointer();
261+
auto metadata = swift::TypeMetadataTrait<T>::getTypeMetadata();
262+
263+
// Dynamic cast will release the error, so we need to retain it.
264+
swift::swift_errorRetain(ep);
265+
bool dynamicCast =
266+
swift::swift_dynamicCast(buffer, &ep, em, metadata,
267+
/*take on success destroy on failure*/ 6);
268+
269+
if (dynamicCast) {
270+
return swift::_impl::implClassFor<T>::type::returnNewValue([&](char *dest) {
271+
swift::_impl::implClassFor<T>::type::initializeWithTake(dest, buffer);
272+
});
273+
}
274+
abort();
275+
// FIXME: return nil.
276+
}
277+
212278
private:
213279
void * _Nonnull opaqueValue = nullptr;
214280
};

test/Interop/SwiftToCxx/functions/swift-functions-errors-execution.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %empty-directory(%t)
22

3-
// RUN: %target-swift-frontend %S/swift-functions-errors.swift -typecheck -module-name Functions -clang-header-expose-decls=all-public -emit-clang-header-path %t/functions.h
3+
// RUN: %target-swift-frontend %S/swift-functions-errors.swift -typecheck -module-name Functions -clang-header-expose-decls=has-expose-attr -emit-clang-header-path %t/functions.h
44

55
// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-functions-errors-execution.o
66
// RUN: %target-interop-build-swift %S/swift-functions-errors.swift -o %t/swift-functions-errors-execution -Xlinker %t/swift-functions-errors-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain
@@ -9,6 +9,7 @@
99
// RUN: %target-run %t/swift-functions-errors-execution | %FileCheck %s
1010

1111
// REQUIRES: executable_test
12+
// UNSUPPORTED: OS=windows-msvc
1213

1314
#include <cassert>
1415
#include <cstdio>
@@ -27,7 +28,9 @@ int main() {
2728
try {
2829
Functions::throwFunction();
2930
} catch (swift::Error& e) {
30-
printf("Exception\n");
31+
auto errorVal = e.as<Functions::NaiveErrors>();
32+
assert(errorVal == Functions::NaiveErrors::throwError);
33+
errorVal.getMessage();
3134
}
3235
try {
3336
Functions::throwFunctionWithReturn();
@@ -43,7 +46,7 @@ int main() {
4346

4447
// CHECK: passEmptyThrowFunction
4548
// CHECK-NEXT: passThrowFunction
46-
// CHECK-NEXT: Exception
49+
// CHECK-NEXT: throwError
4750
// CHECK-NEXT: passThrowFunctionWithReturn
4851
// CHECK-NEXT: Exception
49-
// CHECK-NEXT: Test destroyed
52+
// CHECK-NEXT: Test destroyed

test/Interop/SwiftToCxx/functions/swift-functions-errors.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-decls=all-public -emit-clang-header-path %t/functions.h
2+
// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-decls=has-expose-attr -emit-clang-header-path %t/functions.h
33
// RUN: %FileCheck %s < %t/functions.h
44

55
// RUN: %check-interop-cxx-header-in-clang(%t/functions.h)
@@ -13,11 +13,17 @@
1313

1414
// CHECK: }
1515

16-
enum NaiveErrors : Error {
16+
@_expose(Cxx)
17+
public enum NaiveErrors : Error {
1718
case returnError
1819
case throwError
20+
21+
public func getMessage() {
22+
print(self)
23+
}
1924
}
2025

26+
@_expose(Cxx)
2127
public func emptyThrowFunction() throws { print("passEmptyThrowFunction") }
2228

2329
// CHECK: inline void emptyThrowFunction() {
@@ -34,10 +40,12 @@ class TestDestroyed {
3440
}
3541
}
3642

43+
@_expose(Cxx)
3744
public struct DestroyedError : Error {
3845
let t = TestDestroyed()
3946
}
4047

48+
@_expose(Cxx)
4149
public func testDestroyedError() throws { throw DestroyedError() }
4250

4351
// CHECK: inline void testDestroyedError() {
@@ -48,6 +56,7 @@ public func testDestroyedError() throws { throw DestroyedError() }
4856
// CHECK: throw (swift::Error(opaqueError))
4957
// CHECK: }
5058

59+
@_expose(Cxx)
5160
public func throwFunction() throws {
5261
print("passThrowFunction")
5362
throw NaiveErrors.throwError
@@ -61,6 +70,7 @@ public func throwFunction() throws {
6170
// CHECK: throw (swift::Error(opaqueError))
6271
// CHECK: }
6372

73+
@_expose(Cxx)
6474
public func throwFunctionWithReturn() throws -> Int {
6575
print("passThrowFunctionWithReturn")
6676
throw NaiveErrors.returnError

0 commit comments

Comments
 (0)