On 15/07/16 12:47 +0100, Jonathan Wakely wrote:
diff --git a/libstdc++-v3/libsupc++/pbase_type_info.cc 
b/libstdc++-v3/libsupc++/pbase_type_info.cc
index d47fb23..a2993e4 100644
--- a/libstdc++-v3/libsupc++/pbase_type_info.cc
+++ b/libstdc++-v3/libsupc++/pbase_type_info.cc
@@ -38,6 +38,31 @@ __do_catch (const type_info *thr_type,
    return true;      // same type

#if __cpp_rtti
+  if (*thr_type == typeid (nullptr))
+    {
+      // A catch handler for any pointer type matches nullptr_t.
+      if (typeid (*this) == typeid(__pointer_type_info))
+        {
+          *thr_obj = nullptr;
+          return true;
+        }
+      else if (typeid (*this) == typeid(__pointer_to_member_type_info))
+        {
+          if (__pointee->__is_function_p ())
+            {
+              // A pointer-to-member-function is two words <ptr,adj> but the
+              // nullptr_t exception object at *(nullptr_t*)*thr_obj is only
+              // one word, so we can't safely return it as a PMF. FIXME.
+              return false;
+            }
+          else
+            {
+              *(ptrdiff_t*)*thr_obj = -1; // null pointer to data member

This store could race with accesses in other threads, if the same
exception object gets thrown in multiple threads and the value of the
pointer-to-data-member is read concurrently with the write:

#include <exception>
#include <thread>
#include <cassert>

std::exception_ptr p;

struct A { };

void t()
{
 try {
   std::rethrow_exception(p);
 } catch (int A::* const& e) {
   assert( e == nullptr );
 }
}

int main()
{
 try {
   throw nullptr;
 } catch (...) {
   p = std::current_exception();
 }
 std::thread t1(t);
 std::thread t2(t);
 t1.join();
 t2.join();
}


We could use __atomic_store to do the write, but the reads would still
be non-atomic. The attached patch makes __cxa_throw do that write on
the initial throw, so that a thrown nullptr_t always has the right
content to act as a null pointer-to-member.  However that adds
overhead for every throw. A more efficient solution might be for the
front-end to do that write when it sees a nullptr_t being thrown.


diff --git a/libstdc++-v3/libsupc++/eh_throw.cc b/libstdc++-v3/libsupc++/eh_throw.cc
index 9aac218..e6b155f 100644
--- a/libstdc++-v3/libsupc++/eh_throw.cc
+++ b/libstdc++-v3/libsupc++/eh_throw.cc
@@ -62,6 +62,11 @@ __cxxabiv1::__cxa_throw (void *obj, std::type_info *tinfo,
 {
   PROBE2 (throw, obj, tinfo);
 
+#if __cpp_rtti
+  if (*tinfo == typeid(nullptr))
+    *(ptrdiff_t*)obj = -1; // make it act as a null pointer-to-data-member
+#endif
+
   __cxa_eh_globals *globals = __cxa_get_globals ();
   globals->uncaughtExceptions += 1;
 
diff --git a/libstdc++-v3/libsupc++/pbase_type_info.cc b/libstdc++-v3/libsupc++/pbase_type_info.cc
index a2993e4..4cb626c 100644
--- a/libstdc++-v3/libsupc++/pbase_type_info.cc
+++ b/libstdc++-v3/libsupc++/pbase_type_info.cc
@@ -57,7 +57,7 @@ __do_catch (const type_info *thr_type,
             }
           else
             {
-              *(ptrdiff_t*)*thr_obj = -1; // null pointer to data member
+              // __cxa_throw already wrote (ptrdiff_t)-1 to the object.
               return true;
             }
         }

Reply via email to