diff --git a/test/ClangImporter/Inputs/custom-modules/CxxExceptions.h b/test/ClangImporter/Inputs/custom-modules/CxxExceptions.h new file mode 100644 index 0000000000000..532cd193d3325 --- /dev/null +++ b/test/ClangImporter/Inputs/custom-modules/CxxExceptions.h @@ -0,0 +1,20 @@ +class MyException { +public: + virtual ~MyException() {} +}; + +// Calls f and catches any exceptions thrown by f. +// Returns whether an exception was caught. +inline bool callAndCatchExceptions(void (*f)()) { + try { + f(); + } catch (...) { + return true; + } + + return false; +} + +inline void throwException() { + throw MyException(); +} diff --git a/test/ClangImporter/Inputs/custom-modules/module.map b/test/ClangImporter/Inputs/custom-modules/module.map index b990dcc547b5e..a5cc70240236b 100644 --- a/test/ClangImporter/Inputs/custom-modules/module.map +++ b/test/ClangImporter/Inputs/custom-modules/module.map @@ -41,6 +41,11 @@ module CoreCooling { export * } +module CxxExceptions { + header "CxxExceptions.h" + link "stdc++" +} + module CXXInterop { header "cxx_interop.h" } diff --git a/test/ClangImporter/cxx-exceptions-executable.swift b/test/ClangImporter/cxx-exceptions-executable.swift new file mode 100644 index 0000000000000..6522e38e52b9c --- /dev/null +++ b/test/ClangImporter/cxx-exceptions-executable.swift @@ -0,0 +1,40 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -I %S/Inputs/custom-modules/ -o %t/cxx_exceptions -Xfrontend -enable-cxx-interop +// RUN: %target-codesign %t/cxx_exceptions +// RUN: %target-run %t/cxx_exceptions + +import CxxExceptions +import StdlibUnittest + +var CxxExceptionsTestSuite = TestSuite("CxxExceptions") + +// Uncaught C++ exceptions terminate the program. +CxxExceptionsTestSuite.test("UncaughtException") { + expectCrashLater(withMessage: "terminate called") + throwException() +} + +// Exceptions can be thrown and caught within C++ code if they don't cross any +// interop boundaries. In addition, ClangImporter correctly codegens throw and +// try-catch in a C++ inline function. +CxxExceptionsTestSuite.test("ExceptionCaughtWithinCpp") { + expectTrue(callAndCatchExceptions(throwException)) +} + +// Exceptions cannot be thrown across interop boundaries. If while unwinding the +// stack we reach a Swift stack frame, the program terminates. +// FIXME: This test documents the current behavior, which is wrong. Currently, +// exceptions will propagate through Swift code. This is bad, because Swift +// stack frames will be unwound without doing any potentially necessary +// cleanups. +// I'm documenting the wrong behavior instead of making this an XFAIL because I +// want to show exactly how this fails today. +CxxExceptionsTestSuite.test("DontUnwindAcrossSwiftStackFrame") { + func callThrowException() { + throwException() + } + + expectTrue(callAndCatchExceptions(callThrowException)) +} + +runAllTests()