Skip to content

[Interop] [SwiftToCxx] Introduce Dynamic Cast to swift::Error #61626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
3 changes: 2 additions & 1 deletion lib/PrintAsClang/ModuleContentsWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,8 @@ class ModuleWriter {

SmallVector<ProtocolConformance *, 1> conformances;
auto errorTypeProto = ctx.getProtocol(KnownProtocolKind::Error);
if (ED->lookupConformance(errorTypeProto, conformances)) {
if (outputLangMode != OutputLanguageMode::Cxx
&& ED->lookupConformance(errorTypeProto, conformances)) {
bool hasDomainCase = std::any_of(ED->getAllElements().begin(),
ED->getAllElements().end(),
[](const EnumElementDecl *elem) {
Expand Down
66 changes: 66 additions & 0 deletions lib/PrintAsClang/_SwiftCxxInteroperability.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#if defined(_WIN32)
#include <malloc.h>
#endif
#if !defined(SWIFT_CALL)
# define SWIFT_CALL __attribute__((swiftcall))
#endif

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

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

extern "C" int $ss5ErrorMp; // external global %swift.protocol, align 4

extern "C"
const void * _Nullable
swift_getTypeByMangledNameInContext(
const char *_Nullable typeNameStart,
size_t typeNameLength,
const void *_Nullable context,
const void *_Nullable const *_Nullable genericArgs) SWIFT_CALL;

extern "C" bool swift_dynamicCast(void *_Nullable dest, void *_Nullable src,
const void *_Nullable srcType,
const void * _Nullable targetType,
uint32_t flags);

struct SymbolicP {
alignas(2) uint8_t _1;
uint32_t _2;
uint8_t _3[2];
uint8_t _4;
} __attribute__((packed));

inline const void *_Nullable getErrorMetadata() {
static swift::SymbolicP errorSymbol;
static int *_Nonnull got_ss5ErrorMp = &$ss5ErrorMp;
errorSymbol._1 = 2;
errorSymbol._2 = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&got_ss5ErrorMp) - reinterpret_cast<uintptr_t>(&errorSymbol._2));
errorSymbol._3[0] = '_';
errorSymbol._3[1] = 'p';
errorSymbol._4 = 0;
static_assert(sizeof(errorSymbol) == 8, "");
auto charErrorSymbol = reinterpret_cast<const char *>(&errorSymbol);

const void *ptr2 =
swift::swift_getTypeByMangledNameInContext(charErrorSymbol,
sizeof(errorSymbol) - 1,
nullptr, nullptr);
return ptr2;
}

class Error {
public:
Error() {}
Expand All @@ -209,6 +252,29 @@ class Error {
opaqueValue = other.opaqueValue;
}

// FIXME: Return a Swift::Optional instead.
template<class T>
T as() {
alignas(alignof(T)) char buffer[sizeof(T)];
const void *em = getErrorMetadata();
void *ep = getPointerToOpaquePointer();
auto metadata = swift::TypeMetadataTrait<T>::getTypeMetadata();

// Dynamic cast will release the error, so we need to retain it.
swift::swift_errorRetain(ep);
bool dynamicCast =
swift::swift_dynamicCast(buffer, &ep, em, metadata,
/*take on success destroy on failure*/ 6);

if (dynamicCast) {
return swift::_impl::implClassFor<T>::type::returnNewValue([&](char *dest) {
swift::_impl::implClassFor<T>::type::initializeWithTake(dest, buffer);
});
}
abort();
// FIXME: return nil.
}

private:
void * _Nonnull opaqueValue = nullptr;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)

// 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
// 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

// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-functions-errors-execution.o
// 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
Expand All @@ -9,6 +9,7 @@
// RUN: %target-run %t/swift-functions-errors-execution | %FileCheck %s

// REQUIRES: executable_test
// UNSUPPORTED: OS=windows-msvc

#include <cassert>
#include <cstdio>
Expand All @@ -27,7 +28,9 @@ int main() {
try {
Functions::throwFunction();
} catch (swift::Error& e) {
printf("Exception\n");
auto errorVal = e.as<Functions::NaiveErrors>();
assert(errorVal == Functions::NaiveErrors::throwError);
errorVal.getMessage();
}
try {
Functions::throwFunctionWithReturn();
Expand All @@ -43,7 +46,7 @@ int main() {

// CHECK: passEmptyThrowFunction
// CHECK-NEXT: passThrowFunction
// CHECK-NEXT: Exception
// CHECK-NEXT: throwError
// CHECK-NEXT: passThrowFunctionWithReturn
// CHECK-NEXT: Exception
// CHECK-NEXT: Test destroyed
// CHECK-NEXT: Test destroyed
14 changes: 12 additions & 2 deletions test/Interop/SwiftToCxx/functions/swift-functions-errors.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-decls=all-public -emit-clang-header-path %t/functions.h
// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-decls=has-expose-attr -emit-clang-header-path %t/functions.h
// RUN: %FileCheck %s < %t/functions.h

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

// CHECK: }

enum NaiveErrors : Error {
@_expose(Cxx)
public enum NaiveErrors : Error {
case returnError
case throwError

public func getMessage() {
print(self)
}
}

@_expose(Cxx)
public func emptyThrowFunction() throws { print("passEmptyThrowFunction") }

// CHECK: inline void emptyThrowFunction() {
Expand All @@ -34,10 +40,12 @@ class TestDestroyed {
}
}

@_expose(Cxx)
public struct DestroyedError : Error {
let t = TestDestroyed()
}

@_expose(Cxx)
public func testDestroyedError() throws { throw DestroyedError() }

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

@_expose(Cxx)
public func throwFunction() throws {
print("passThrowFunction")
throw NaiveErrors.throwError
Expand All @@ -61,6 +70,7 @@ public func throwFunction() throws {
// CHECK: throw (swift::Error(opaqueError))
// CHECK: }

@_expose(Cxx)
public func throwFunctionWithReturn() throws -> Int {
print("passThrowFunctionWithReturn")
throw NaiveErrors.returnError
Expand Down