Add error_scope to py::class_::dealloc() to protect destructor calls (#2342)

Fixes issue #1878
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index 87f4645..d34c92c 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -1388,6 +1388,13 @@
 
     /// Deallocates an instance; via holder, if constructed; otherwise via operator delete.
     static void dealloc(detail::value_and_holder &v_h) {
+        // We could be deallocating because we are cleaning up after a Python exception.
+        // If so, the Python error indicator will be set. We need to clear that before
+        // running the destructor, in case the destructor code calls more Python.
+        // If we don't, the Python API will exit with an exception, and pybind11 will
+        // throw error_already_set from the C++ destructor which is forbidden and triggers
+        // std::terminate().
+        error_scope scope;
         if (v_h.holder_constructed()) {
             v_h.holder<holder_type>().~holder_type();
             v_h.set_holder_constructed(false);
diff --git a/tests/test_class.cpp b/tests/test_class.cpp
index 7edcdce..5369cb0 100644
--- a/tests/test_class.cpp
+++ b/tests/test_class.cpp
@@ -379,6 +379,17 @@
     // test_non_final_final
     struct IsNonFinalFinal {};
     py::class_<IsNonFinalFinal>(m, "IsNonFinalFinal", py::is_final());
+
+    struct PyPrintDestructor {
+        PyPrintDestructor() {}
+        ~PyPrintDestructor() {
+            py::print("Print from destructor");
+        }
+        void throw_something() { throw std::runtime_error("error"); }
+    };
+    py::class_<PyPrintDestructor>(m, "PyPrintDestructor")
+        .def(py::init<>())
+        .def("throw_something", &PyPrintDestructor::throw_something);
 }
 
 template <int N> class BreaksBase { public:
diff --git a/tests/test_class.py b/tests/test_class.py
index c38a5e8..bbf8481 100644
--- a/tests/test_class.py
+++ b/tests/test_class.py
@@ -323,3 +323,9 @@
         class PyNonFinalFinalChild(m.IsNonFinalFinal):
             pass
     assert str(exc_info.value).endswith("is not an acceptable base type")
+
+
+# https://github.com/pybind/pybind11/issues/1878
+def test_exception_rvalue_abort():
+    with pytest.raises(RuntimeError):
+        m.PyPrintDestructor().throw_something()