diff --git a/lib/PrintAsClang/ModuleContentsWriter.cpp b/lib/PrintAsClang/ModuleContentsWriter.cpp index 08d7a5ee4faea..b1225f988a787 100644 --- a/lib/PrintAsClang/ModuleContentsWriter.cpp +++ b/lib/PrintAsClang/ModuleContentsWriter.cpp @@ -780,6 +780,7 @@ EmittedClangHeaderDependencyInfo swift::printModuleContentsAsCxx( os << "#ifndef SWIFT_CXX_INTEROP_HIDE_STL_OVERLAY\n"; os << "#include \n"; os << "#endif\n"; + os << "#include \n"; // Embed an overlay for the standard library. ClangSyntaxPrinter(moduleOS).printIncludeForShimHeader( "_SwiftStdlibCxxOverlay.h"); diff --git a/lib/PrintAsClang/PrintClangFunction.cpp b/lib/PrintAsClang/PrintClangFunction.cpp index 21f7a634961c6..bedafb6b0ade6 100644 --- a/lib/PrintAsClang/PrintClangFunction.cpp +++ b/lib/PrintAsClang/PrintClangFunction.cpp @@ -688,6 +688,8 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature( ClangRepresentation::representable; // Print out the return type. + if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx) + os << "Swift::ThrowingResult<"; if (kind == FunctionSignatureKind::CFunctionProto) { // First, verify that the C++ return type is representable. { @@ -740,7 +742,8 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature( .isUnsupported()) return resultingRepresentation; } - + if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx) + os << ">"; os << ' '; if (const auto *typeDecl = modifiers.qualifierContext) ClangSyntaxPrinter(os).printNominalTypeQualifier( @@ -1241,14 +1244,43 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody( // Create the condition and the statement to throw an exception. if (hasThrows) { os << " if (opaqueError != nullptr)\n"; + os << "#ifdef __cpp_exceptions\n"; os << " throw (Swift::Error(opaqueError));\n"; - } + os << "#else\n"; + if (resultTy->isVoid()) { + os << " return SWIFT_RETURN_THUNK(void, Swift::Error(opaqueError));\n"; + os << "#endif\n"; + } else { + auto directResultType = signature.getDirectResultType(); + printDirectReturnOrParamCType( + *directResultType, resultTy, moduleContext, os, cPrologueOS, + typeMapping, interopContext, [&]() { + os << " return SWIFT_RETURN_THUNK("; + OptionalTypeKind retKind; + Type objTy; + std::tie(objTy, retKind) = + DeclAndTypePrinter::getObjectTypeAndOptionality(FD, resultTy); - // Return the function result value if it doesn't throw. - if (!resultTy->isVoid() && hasThrows) { - os << "\n"; - os << "return returnValue;\n"; + auto s = printClangFunctionReturnType(objTy, retKind, const_cast(moduleContext), + OutputLanguageMode::Cxx); + os << ", Swift::Error(opaqueError));\n"; + os << "#endif\n"; + + // Return the function result value if it doesn't throw. + if (!resultTy->isVoid() && hasThrows) { + os << "\n"; + os << " return SWIFT_RETURN_THUNK("; + printClangFunctionReturnType( + objTy, retKind, const_cast(moduleContext), + OutputLanguageMode::Cxx); + os << ", returnValue);\n"; + } + + assert(!s.isUnsupported()); + }); + } } + } static StringRef getConstructorName(const AbstractFunctionDecl *FD) { diff --git a/lib/PrintAsClang/_SwiftStdlibCxxOverlay.h b/lib/PrintAsClang/_SwiftStdlibCxxOverlay.h index f610642ff0283..8ceecb79a300a 100644 --- a/lib/PrintAsClang/_SwiftStdlibCxxOverlay.h +++ b/lib/PrintAsClang/_SwiftStdlibCxxOverlay.h @@ -132,6 +132,8 @@ inline const void *_Nullable getErrorMetadata() { return ptr2; } +#ifndef SWIFT_CXX_INTEROP_HIDE_SWIFT_ERROR + class Error { public: Error() {} @@ -179,4 +181,196 @@ class Error { void *_Nonnull opaqueValue = nullptr; }; +namespace _impl { + +constexpr inline std::size_t max(std::size_t a, std::size_t b) { + return a > b ? a : b; +} + +} // namespace _impl + +/// The Expected class has either an error or an value. +template +class Expected { +public: + + /// Default + constexpr Expected() noexcept { + new (&buffer) Error(); + has_val = false; + } + + constexpr Expected(const Swift::Error& error_val) noexcept { + new (&buffer) Error(error_val); + has_val = false; + } + + constexpr Expected(const T &val) noexcept { + new (&buffer) T(val); + has_val = true; + } + + /// Copy + constexpr Expected(Expected const& other) noexcept { + if (other.has_value()) + new (&buffer) T(other.value()); + else + new (&buffer) Error(other.error()); + + has_val = other.has_value(); + } + + /// Move + // FIXME: Implement move semantics when move Swift values is possible + constexpr Expected(Expected&&) noexcept { abort(); } + + ~Expected() noexcept { + if (has_value()) + reinterpret_cast(buffer)->~T(); + else + reinterpret_cast(buffer)->~Error(); + } + + /// assignment + constexpr auto operator=(Expected&& other) noexcept = delete; + constexpr auto operator=(Expected&) noexcept = delete; + + /// For accessing T's members + constexpr T const *_Nonnull operator->() const noexcept { + if (!has_value()) + abort(); + return reinterpret_cast(buffer); + } + + constexpr T *_Nonnull operator->() noexcept { + if (!has_value()) + abort(); + return reinterpret_cast(buffer); + } + + /// Getting reference to T + constexpr T const &operator*() const & noexcept { + if (!has_value()) + abort(); + return reinterpret_cast(buffer); + } + + constexpr T &operator*() & noexcept { + if (!has_value()) + abort(); + return reinterpret_cast(buffer); + } + + constexpr explicit operator bool() const noexcept { return has_value(); } + + // Get value, if not exists abort + constexpr T const& value() const& { + if (!has_value()) + abort(); + return *reinterpret_cast(buffer); + } + + constexpr T& value() & { + if (!has_value()) + abort(); + return *reinterpret_cast(buffer); + } + + // Get error + constexpr Swift::Error const& error() const& { + if (has_value()) + abort(); + return reinterpret_cast(buffer); + } + + constexpr Swift::Error& error() & { + if (has_value()) + abort(); + return reinterpret_cast(buffer); + } + + constexpr bool has_value() const noexcept { return has_val; } + +private: + alignas(_impl::max(alignof(T), alignof(Swift::Error))) char buffer[_impl::max(sizeof(T), sizeof(Swift::Error))]; + bool has_val; +}; + +template<> +class Expected { +public: + /// Default + Expected() noexcept { + new (&buffer) Error(); + has_val = false; + } + + Expected(const Swift::Error& error_val) noexcept { + new (&buffer) Error(error_val); + has_val = false; + } + + + /// Copy + Expected(Expected const& other) noexcept { + if (other.has_value()) + abort(); + else + new (&buffer) Error(other.error()); + + has_val = other.has_value(); + } + + /// Move + // FIXME: Implement move semantics when move swift values is possible + [[noreturn]] Expected(Expected&&) noexcept { abort(); } + + ~Expected() noexcept { + reinterpret_cast(buffer)->~Error(); + } + + /// assignment + constexpr auto operator=(Expected&& other) noexcept = delete; + constexpr auto operator=(Expected&) noexcept = delete; + + + constexpr explicit operator bool() const noexcept { return has_value(); } + + // Get error + constexpr Swift::Error const& error() const& { + if (has_value()) + abort(); + return reinterpret_cast(buffer); + } + + constexpr Swift::Error& error() & { + if (has_value()) + abort(); + return reinterpret_cast(buffer); + } + + constexpr bool has_value() const noexcept { return has_val; } +private: + alignas(alignof(Swift::Error)) char buffer[sizeof(Swift::Error)]; + bool has_val; +}; + +#ifdef __cpp_exceptions + +template +using ThrowingResult = T; + +#define SWIFT_RETURN_THUNK(T, v) v + +#else + +template +using ThrowingResult = Swift::Expected; + +#define SWIFT_RETURN_THUNK(T, v) Swift::Expected(v) + +#endif + +#endif // SWIFT_CXX_INTEROP_HIDE_SWIFT_ERROR + #endif diff --git a/test/Interop/SwiftToCxx/functions/swift-expected-execution.cpp b/test/Interop/SwiftToCxx/functions/swift-expected-execution.cpp new file mode 100644 index 0000000000000..779299de4a3ae --- /dev/null +++ b/test/Interop/SwiftToCxx/functions/swift-expected-execution.cpp @@ -0,0 +1,118 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend %S/swift-functions-errors.swift -typecheck -module-name Functions -Xcc -fno-exceptions -enable-experimental-cxx-interop -emit-clang-header-path %t/functions.h + +// RUN: %target-interop-build-clangxx -c %s -I %t -fno-exceptions -o %t/swift-expected-execution.o +// RUN: %target-interop-build-swift %S/swift-functions-errors.swift -o %t/swift-expected-execution -Xlinker %t/swift-expected-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain + +// RUN: %target-codesign %t/swift-expected-execution +// RUN: %target-run %t/swift-expected-execution | %FileCheck %s + +// REQUIRES: executable_test +// UNSUPPORTED: OS=windows-msvc + +#include +#include +#include "functions.h" + +int main() { + + // Test Empty Constructor + auto testIntEmpty = Swift::Expected(); + + // Test Error Constructor + Swift::Error e; + auto testIntError = Swift::Expected(e); + + // Test Value Constructor + auto testIntValue = Swift::Expected(42); + + // Test Copy Constructor + auto testCopy = testIntEmpty; + + // TODO: Test Move Constructor + + // Test Destructor + auto testDestEmpty = Swift::Expected(); + auto testDestInt = Swift::Expected(42); + auto testDestError = Swift::Expected(e); + testDestEmpty.~Expected(); + testDestInt.~Expected(); + testDestError.~Expected(); + + // TODO: Test Assignment (Move) + + // Test Access to T's members (const) + const auto exp = testIntValue; + if (*exp.operator->() == 42) + printf("Test Access to T's members (const)\n"); + + // Test Access to T's members + if (*testIntValue.operator->() == 42) + printf("Test Access to T's members\n"); + + // Test Reference to T's members (const) + const auto refExp = testIntValue; + if (*refExp == 42) + printf("Test Reference to T's members (const)\n"); + + // Test Reference to T's members + if (*testIntValue == 42) + printf("Test Reference to T's members\n"); + + // Test bool operator + if (testIntValue) { + printf("Test operator bool\n"); + } + + const auto constExpectedResult = Functions::throwFunctionWithPossibleReturn(0); + if (!constExpectedResult.has_value()) { + auto constError = constExpectedResult.error(); + auto optionalError = constError.as(); + assert(optionalError.isSome()); + auto valueError = optionalError.get(); + assert(valueError == Functions::NaiveErrors::returnError); + valueError.getMessage(); + } + + auto expectedResult = Functions::throwFunctionWithPossibleReturn(0); + if (!expectedResult.has_value()) { + auto error = expectedResult.error(); + auto optionalError = error.as(); + assert(optionalError.isSome()); + auto valueError = optionalError.get(); + assert(valueError == Functions::NaiveErrors::returnError); + valueError.getMessage(); + } + + // Test get T's Value (const) + const auto valueExp = testIntValue; + if (valueExp.value() == 42) + printf("Test get T's Value (const)\n"); + + // Test get T's Value + if (testIntValue.value() == 42) + printf("Test get T's Value\n"); + + // Test has Value + if (testIntValue.has_value()) + printf("testIntValue has a value\n"); + if (!testIntError.has_value()) + printf("testIntError doesn't have a value\n"); + + return 0; +} + +// CHECK: Test Access to T's members (const) +// CHECK-NEXT: Test Access to T's members +// CHECK-NEXT: Test Reference to T's members (const) +// CHECK-NEXT: Test Reference to T's members +// CHECK-NEXT: Test operator bool +// CHECK-NEXT: passThrowFunctionWithPossibleReturn +// CHECK-NEXT: returnError +// CHECK-NEXT: passThrowFunctionWithPossibleReturn +// CHECK-NEXT: returnError +// CHECK-NEXT: Test get T's Value (const) +// CHECK-NEXT: Test get T's Value +// CHECK-NEXT: testIntValue has a value +// CHECK-NEXT: testIntError doesn't have a value diff --git a/test/Interop/SwiftToCxx/functions/swift-functions-errors.swift b/test/Interop/SwiftToCxx/functions/swift-functions-errors.swift index a7fd6b42d2b6a..2c670ffc2ab3e 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions-errors.swift +++ b/test/Interop/SwiftToCxx/functions/swift-functions-errors.swift @@ -9,7 +9,11 @@ // CHECK-LABEL: namespace _impl { // CHECK: SWIFT_EXTERN void $s9Functions18emptyThrowFunctionyyKF(SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // emptyThrowFunction() +// CHECK: SWIFT_EXTERN void $s9Functions18testDestroyedErroryyKF(SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // testDestroyedError() // CHECK: SWIFT_EXTERN void $s9Functions13throwFunctionyyKF(SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // throwFunction() +// CHECK: SWIFT_EXTERN ptrdiff_t $s9Functions31throwFunctionWithPossibleReturnyS2iKF(ptrdiff_t a, SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // throwFunctionWithPossibleReturn(_:) +// CHECK: SWIFT_EXTERN ptrdiff_t $s9Functions23throwFunctionWithReturnSiyKF(SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // throwFunctionWithReturn() + // CHECK: } @@ -26,12 +30,16 @@ public enum NaiveErrors : Error { @_expose(Cxx) public func emptyThrowFunction() throws { print("passEmptyThrowFunction") } -// CHECK: inline void emptyThrowFunction() { +// CHECK: inline Swift::ThrowingResult emptyThrowFunction() { // CHECK: void* opaqueError = nullptr; // CHECK: void* _ctx = nullptr; // CHECK: _impl::$s9Functions18emptyThrowFunctionyyKF(_ctx, &opaqueError); // CHECK: if (opaqueError != nullptr) -// CHECK: throw (Swift::Error(opaqueError)) +// CHECK: #ifdef __cpp_exceptions +// CHECK: throw (Swift::Error(opaqueError)); +// CHECK: #else +// CHECK: return SWIFT_RETURN_THUNK(void, Swift::Error(opaqueError)); +// CHECK: #endif // CHECK: } class TestDestroyed { @@ -48,12 +56,16 @@ public struct DestroyedError : Error { @_expose(Cxx) public func testDestroyedError() throws { throw DestroyedError() } -// CHECK: inline void testDestroyedError() { +// CHECK: inline Swift::ThrowingResult testDestroyedError() { // CHECK: void* opaqueError = nullptr; // CHECK: void* _ctx = nullptr; // CHECK: _impl::$s9Functions18testDestroyedErroryyKF(_ctx, &opaqueError); // CHECK: if (opaqueError != nullptr) -// CHECK: throw (Swift::Error(opaqueError)) +// CHECK: #ifdef __cpp_exceptions +// CHECK: throw (Swift::Error(opaqueError)); +// CHECK: #else +// CHECK: return SWIFT_RETURN_THUNK(void, Swift::Error(opaqueError)); +// CHECK: #endif // CHECK: } @_expose(Cxx) @@ -62,12 +74,38 @@ public func throwFunction() throws { throw NaiveErrors.throwError } -// CHECK: inline void throwFunction() { +// CHECK: inline Swift::ThrowingResult throwFunction() { // CHECK: void* opaqueError = nullptr; // CHECK: void* _ctx = nullptr; // CHECK: _impl::$s9Functions13throwFunctionyyKF(_ctx, &opaqueError); // CHECK: if (opaqueError != nullptr) -// CHECK: throw (Swift::Error(opaqueError)) +// CHECK: #ifdef __cpp_exceptions +// CHECK: throw (Swift::Error(opaqueError)); +// CHECK: #else +// CHECK: return SWIFT_RETURN_THUNK(void, Swift::Error(opaqueError)); +// CHECK: #endif +// CHECK: } + +@_expose(Cxx) +public func throwFunctionWithPossibleReturn(_ a: Int) throws -> Int { + print("passThrowFunctionWithPossibleReturn") + if (a == 0) { + throw NaiveErrors.returnError + } + return 0 +} + +// CHECK: inline Swift::ThrowingResult throwFunctionWithPossibleReturn(swift::Int a) SWIFT_WARN_UNUSED_RESULT { +// CHECK: void* opaqueError = nullptr; +// CHECK: void* _ctx = nullptr; +// CHECK: auto returnValue = _impl::$s9Functions31throwFunctionWithPossibleReturnyS2iKF(a, _ctx, &opaqueError); +// CHECK: if (opaqueError != nullptr) +// CHECK: #ifdef __cpp_exceptions +// CHECK: throw (Swift::Error(opaqueError)); +// CHECK: #else +// CHECK: return SWIFT_RETURN_THUNK(swift::Int, Swift::Error(opaqueError)); +// CHECK: #endif +// CHECK: return SWIFT_RETURN_THUNK(swift::Int, returnValue); // CHECK: } @_expose(Cxx) @@ -77,11 +115,14 @@ public func throwFunctionWithReturn() throws -> Int { return 0 } -// CHECK: inline swift::Int throwFunctionWithReturn() SWIFT_WARN_UNUSED_RESULT { +// CHECK: inline Swift::ThrowingResult throwFunctionWithReturn() SWIFT_WARN_UNUSED_RESULT { // CHECK: void* opaqueError = nullptr; // CHECK: void* _ctx = nullptr; // CHECK: auto returnValue = _impl::$s9Functions23throwFunctionWithReturnSiyKF(_ctx, &opaqueError); -// CHECK: if (opaqueError != nullptr) -// CHECK: throw (Swift::Error(opaqueError)) -// CHECK: return returnValue; +// CHECK: #ifdef __cpp_exceptions +// CHECK: throw (Swift::Error(opaqueError)); +// CHECK: #else +// CHECK: return SWIFT_RETURN_THUNK(swift::Int, Swift::Error(opaqueError)); +// CHECK: #endif +// CHECK: return SWIFT_RETURN_THUNK(swift::Int, returnValue); // CHECK: } diff --git a/test/Interop/SwiftToCxxToSwift/hide-swift-module-namespace-in-swift.swift b/test/Interop/SwiftToCxxToSwift/hide-swift-module-namespace-in-swift.swift index d77def8cc5514..6e7025ae2c8de 100644 --- a/test/Interop/SwiftToCxxToSwift/hide-swift-module-namespace-in-swift.swift +++ b/test/Interop/SwiftToCxxToSwift/hide-swift-module-namespace-in-swift.swift @@ -1,19 +1,20 @@ // RUN: %empty-directory(%t) // RUN: split-file %s %t -// RUN: touch %t/swiftMod.h -// RUN: %target-swift-frontend -typecheck %t/swiftMod.swift -typecheck -module-name SwiftMod -emit-clang-header-path %t/swiftMod.h -I %t -enable-experimental-cxx-interop +// RUN: %target-swift-frontend -typecheck %t/swiftMod.swift -typecheck -module-name SwiftMod -emit-clang-header-path %t/swiftMod.h -I %t -enable-experimental-cxx-interop -Xcc -DFIRSTPASS // RUN: %FileCheck %s < %t/swiftMod.h -// RUN: %target-swift-frontend -typecheck %t/swiftMod.swift -typecheck -module-name SwiftMod -emit-clang-header-path %t/swiftMod2.h -I %t -enable-experimental-cxx-interop +// RUN: %target-swift-frontend -typecheck %t/swiftMod.swift -typecheck -module-name SwiftMod -emit-clang-header-path %t/swiftMod2.h -I %t -enable-experimental-cxx-interop -Xcc -DSWIFT_CXX_INTEROP_HIDE_SWIFT_ERROR // RUN: %check-interop-cxx-header-in-clang(%t/swiftMod2.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY -Wno-error) // XFAIL: OS=linux-android, OS=linux-androideabi //--- header.h +#ifndef FIRSTPASS #include "swiftMod.h" +#endif //--- module.modulemap module SwiftToCxxTest {