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;
}
}