From 5217bd6464ff8eee3ae48ffd50f3e1f18d8a9a1a Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Fri, 27 Mar 2020 16:43:11 +0100 Subject: [PATCH 1/2] Add a test documenting the current state of exception handling. Swift currently allows C++ exceptions to propagate across Swift stack frames, which is wrong; this test documents that, but also the current behaviors that are fine. See also the discussion here: https://forums.swift.org/t/handling-c-exceptions/34823 I'm not sure whether this test will on all platforms, particularly Windows. I may restrict it to certain platforms if build bots show it doesn't work everywhere. --- .../Inputs/custom-modules/CxxExceptions.h | 20 ++++++++++ .../Inputs/custom-modules/module.map | 5 +++ .../cxx-exceptions-executable.swift | 39 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 test/ClangImporter/Inputs/custom-modules/CxxExceptions.h create mode 100644 test/ClangImporter/cxx-exceptions-executable.swift 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..3ff7061932710 --- /dev/null +++ b/test/ClangImporter/cxx-exceptions-executable.swift @@ -0,0 +1,39 @@ +// 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. +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() From d7bb8f6e76519982fe6bd1d6cc82d1c4a4189929 Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Fri, 27 Mar 2020 17:14:21 +0100 Subject: [PATCH 2/2] Extend a comment in the test. --- test/ClangImporter/cxx-exceptions-executable.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/ClangImporter/cxx-exceptions-executable.swift b/test/ClangImporter/cxx-exceptions-executable.swift index 3ff7061932710..6522e38e52b9c 100644 --- a/test/ClangImporter/cxx-exceptions-executable.swift +++ b/test/ClangImporter/cxx-exceptions-executable.swift @@ -15,7 +15,8 @@ CxxExceptionsTestSuite.test("UncaughtException") { } // Exceptions can be thrown and caught within C++ code if they don't cross any -// interop boundaries. +// interop boundaries. In addition, ClangImporter correctly codegens throw and +// try-catch in a C++ inline function. CxxExceptionsTestSuite.test("ExceptionCaughtWithinCpp") { expectTrue(callAndCatchExceptions(throwException)) }