[test] Add basic _Unwind_ForcedUnwind + exception tests
Forced unwinding is like a foreign exception, which can be caught by `catch (...)` and rethrown.
If not rethrown, `__cxa_end_cath` will call `_Unwind_DeleteException` to destroy the object.
The behavior going through empty `throw()` and non-empty `throw(int)` is not
clear (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98785), so I do not add such
tests.
Differential Revision: https://reviews.llvm.org/D95200
GitOrigin-RevId: 81af8149d8cb8ff65f2569d813252fab2a14df00
diff --git a/test/forced_unwind1.pass.cpp b/test/forced_unwind1.pass.cpp
new file mode 100644
index 0000000..9432f48
--- /dev/null
+++ b/test/forced_unwind1.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// _Unwind_ForcedUnwind raised exception can be caught by catch (...) and be
+// rethrown. If not rethrown, exception_cleanup will be called.
+
+// UNSUPPORTED: no-exceptions, c++03
+
+#include <stdlib.h>
+#include <string.h>
+#include <unwind.h>
+#include <tuple>
+
+static int bits = 0;
+
+struct C {
+ int bit;
+ C(int b) : bit(b) {}
+ ~C() { bits |= bit; }
+};
+
+template <typename T>
+struct Stop;
+
+template <typename R, typename... Args>
+struct Stop<R (*)(Args...)> {
+ // The third argument of _Unwind_Stop_Fn is uint64_t in Itanium C++ ABI/LLVM
+ // libunwind while _Unwind_Exception_Class in libgcc.
+ typedef typename std::tuple_element<2, std::tuple<Args...>>::type type;
+
+ static _Unwind_Reason_Code stop(int, _Unwind_Action actions, type,
+ struct _Unwind_Exception*,
+ struct _Unwind_Context*, void*) {
+ if (actions & _UA_END_OF_STACK)
+ abort();
+ return _URC_NO_REASON;
+ }
+};
+
+static void cleanup(_Unwind_Reason_Code, struct _Unwind_Exception* exc) {
+ bits |= 8;
+ delete exc;
+}
+
+static void forced_unwind() {
+ _Unwind_Exception* exc = new _Unwind_Exception;
+ exc->exception_class = 0;
+ exc->exception_cleanup = cleanup;
+ _Unwind_ForcedUnwind(exc, Stop<_Unwind_Stop_Fn>::stop, 0);
+ abort();
+}
+
+static void test() {
+ try {
+ C four(4);
+ try {
+ C one(1);
+ forced_unwind();
+ } catch (...) {
+ bits |= 2;
+ throw;
+ }
+ } catch (int) {
+ } catch (...) {
+ // __cxa_end_catch calls cleanup.
+ }
+}
+
+int main() {
+ test();
+ return bits != 15;
+}
diff --git a/test/forced_unwind2.pass.cpp b/test/forced_unwind2.pass.cpp
new file mode 100644
index 0000000..4eb9f48
--- /dev/null
+++ b/test/forced_unwind2.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Forced unwinding causes std::terminate when going through noexcept.
+
+// UNSUPPORTED: no-exceptions, c++03
+
+#include <exception>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unwind.h>
+#include <tuple>
+
+template <typename T>
+struct Stop;
+
+template <typename R, typename... Args>
+struct Stop<R (*)(Args...)> {
+ // The third argument of _Unwind_Stop_Fn is uint64_t in Itanium C++ ABI/LLVM
+ // libunwind while _Unwind_Exception_Class in libgcc.
+ typedef typename std::tuple_element<2, std::tuple<Args...>>::type type;
+
+ static _Unwind_Reason_Code stop(int, _Unwind_Action actions, type,
+ struct _Unwind_Exception*,
+ struct _Unwind_Context*, void*) {
+ if (actions & _UA_END_OF_STACK)
+ abort();
+ return _URC_NO_REASON;
+ }
+};
+
+static void forced_unwind() {
+ _Unwind_Exception* exc = new _Unwind_Exception;
+ exc->exception_class = 0;
+ exc->exception_cleanup = 0;
+ _Unwind_ForcedUnwind(exc, Stop<_Unwind_Stop_Fn>::stop, 0);
+ abort();
+}
+
+static void test() noexcept { forced_unwind(); }
+
+static void terminate() { exit(0); }
+
+int main() {
+ std::set_terminate(terminate);
+ try {
+ test();
+ } catch (...) {
+ }
+ abort();
+}