rsmith created this revision.
rsmith added reviewers: EricWF, mclow.lists.
rsmith added a subscriber: cfe-commits.
rsmith set the repository for this revision to rL LLVM.

This fixes a bug where throwing an exception of type nullptr_t and catching it 
as a pointer-to-member would not guarantee to produce a null value in the catch 
handler. The fix is pretty simple: we statically allocate a constant null 
pointer-to-data-member representation and a constant null 
pointer-to-member-function representation, and produce the address of the 
relevant value as the adjusted pointer for the exception.

Repository:
  rL LLVM

https://reviews.llvm.org/D22528

Files:
  src/CMakeLists.txt
  src/private_typeinfo.cpp
  test/catch_const_pointer_nullptr.pass.cpp
  test/catch_member_pointer_nullptr.pass.cpp
  test/catch_pointer_nullptr.pass.cpp
  test/catch_reference_nullptr.pass.cpp
  test/incomplete_type.sh.cpp

Index: test/incomplete_type.sh.cpp
===================================================================
--- test/incomplete_type.sh.cpp
+++ test/incomplete_type.sh.cpp
@@ -88,7 +88,9 @@
     assert(false);
   } catch (int CompleteAtThrow::*) {
     assert(false);
-  } catch (int NeverDefined::*) {}
+  } catch (int NeverDefined::*p) {
+    assert(!p);
+  }
   AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompleteMP(), typeid(int IncompleteAtThrow::*));
   try {
     ThrowIncompleteMP();
@@ -99,24 +101,30 @@
     assert(false);
   } catch (IncompleteAtThrow**) {
     assert(false);
-  } catch (int IncompleteAtThrow::*) {}
+  } catch (int IncompleteAtThrow::*p) {
+    assert(!p);
+  }
 
   AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompletePP(), typeid(IncompleteAtThrow**));
   try {
     ThrowIncompletePP();
     assert(false);
   } catch (int IncompleteAtThrow::*) {
     assert(false);
-  } catch (IncompleteAtThrow**) {}
+  } catch (IncompleteAtThrow** p) {
+    assert(!p);
+  }
 
   try {
     ThrowIncompletePMP();
     assert(false);
   } catch (int IncompleteAtThrow::*) {
     assert(false);
   } catch (IncompleteAtThrow**) {
     assert(false);
-  } catch (int IncompleteAtThrow::**) {}
+  } catch (int IncompleteAtThrow::**p) {
+    assert(!p);
+  }
 
   AssertIncompleteTypeInfoEquals(ReturnTypeInfoCompleteMP(), typeid(int CompleteAtThrow::*));
   try {
@@ -128,7 +136,9 @@
     assert(false);
   } catch (CompleteAtThrow**) {
     assert(false);
-  } catch (int CompleteAtThrow::*) {}
+  } catch (int CompleteAtThrow::*p) {
+    assert(!p);
+  }
 
   AssertIncompleteTypeInfoEquals(ReturnTypeInfoCompletePP(), typeid(CompleteAtThrow**));
   try {
@@ -140,7 +150,9 @@
     assert(false);
   } catch (int CompleteAtThrow::*) {
     assert(false);
-  } catch (CompleteAtThrow**) {}
+  } catch (CompleteAtThrow**p) {
+    assert(!p);
+  }
 
   try {
     ThrowCompletePMP();
@@ -153,22 +165,30 @@
     assert(false);
   } catch (CompleteAtThrow**) {
     assert(false);
-  } catch (int CompleteAtThrow::**) {}
+  } catch (int CompleteAtThrow::**p) {
+    assert(!p);
+  }
 
 #if __cplusplus >= 201103L
   // Catch nullptr as complete type
   try {
     ThrowNullptr();
-  } catch (int IncompleteAtThrow::*) {}
+  } catch (int IncompleteAtThrow::*p) {
+    assert(!p);
+  }
 
   // Catch nullptr as an incomplete type
   try {
     ThrowNullptr();
-  } catch (int CompleteAtThrow::*) {}
+  } catch (int CompleteAtThrow::*p) {
+    assert(!p);
+  }
   // Catch nullptr as a type that is never complete.
   try {
     ThrowNullptr();
-  } catch (int NeverDefined::*) {}
+  } catch (int NeverDefined::*p) {
+    assert(!p);
+  }
 #endif
 }
 #endif
Index: test/catch_reference_nullptr.pass.cpp
===================================================================
--- test/catch_reference_nullptr.pass.cpp
+++ test/catch_reference_nullptr.pass.cpp
@@ -0,0 +1,49 @@
+//===--------------------- catch_pointer_nullptr.cpp ----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, libcxxabi-no-exceptions
+
+#include <cassert>
+#include <cstdlib>
+
+struct A {};
+
+template<typename T, bool CanCatchNullptr>
+static void catch_nullptr_test() {
+  try {
+    throw nullptr;
+  } catch (T &p) {
+    assert(CanCatchNullptr && !p);
+  } catch (...) {
+    assert(!CanCatchNullptr);
+  }
+}
+
+int main()
+{
+  using nullptr_t = decltype(nullptr);
+
+  // A reference to nullptr_t can catch nullptr.
+  catch_nullptr_test<nullptr_t, true>();
+  catch_nullptr_test<const nullptr_t, true>();
+  catch_nullptr_test<volatile nullptr_t, true>();
+  catch_nullptr_test<const volatile nullptr_t, true>();
+
+  // No other reference type can.
+#if 0
+  // FIXME: These tests fail, because the ABI provides no way for us to
+  // distinguish this from catching by value.
+  catch_nullptr_test<void *, false>();
+  catch_nullptr_test<void * const, false>();
+  catch_nullptr_test<int *, false>();
+  catch_nullptr_test<A *, false>();
+  catch_nullptr_test<int A::*, false>();
+  catch_nullptr_test<int (A::*)(), false>();
+#endif
+}
Index: test/catch_pointer_nullptr.pass.cpp
===================================================================
--- test/catch_pointer_nullptr.pass.cpp
+++ test/catch_pointer_nullptr.pass.cpp
@@ -21,8 +21,9 @@
         throw nullptr;
         assert(false);
     }
-    catch (int*)
+    catch (int* p)
     {
+        assert(!p);
     }
     catch (long*)
     {
@@ -37,8 +38,9 @@
         throw nullptr;
         assert(false);
     }
-    catch (A*)
+    catch (A* p)
     {
+        assert(!p);
     }
     catch (int*)
     {
@@ -51,8 +53,8 @@
   try {
     throw nullptr;
     assert(false);
-  } catch (Catch) {
-    // nothing todo
+  } catch (Catch c) {
+    assert(!c);
   } catch (...) {
     assert(false);
   }
Index: test/catch_member_pointer_nullptr.pass.cpp
===================================================================
--- test/catch_member_pointer_nullptr.pass.cpp
+++ test/catch_member_pointer_nullptr.pass.cpp
@@ -29,8 +29,9 @@
         throw nullptr;
         assert(false);
     }
-    catch (md2)
+    catch (md2 p)
     {
+        assert(!p);
     }
     catch (md1)
     {
@@ -45,8 +46,9 @@
         throw nullptr;
         assert(false);
     }
-    catch (md1)
+    catch (md1 p)
     {
+        assert(!p);
     }
     catch (md2)
     {
Index: test/catch_const_pointer_nullptr.pass.cpp
===================================================================
--- test/catch_const_pointer_nullptr.pass.cpp
+++ test/catch_const_pointer_nullptr.pass.cpp
@@ -29,8 +29,9 @@
         throw nullptr;
         assert(false);
     }
-    catch (A*)
+    catch (A* p)
     {
+        assert(!p);
     }
     catch (const A*)
     {
@@ -46,8 +47,9 @@
         throw nullptr;
         assert(false);
     }
-    catch (const A*)
+    catch (const A* p)
     {
+        assert(!p);
     }
     catch (A*)
     {
@@ -62,8 +64,9 @@
         throw nullptr;
         assert(false);
     }
-    catch (const A* const)
+    catch (const A* const p)
     {
+        assert(!p);
     }
     catch (A*)
     {
@@ -78,8 +81,9 @@
         throw nullptr;
         assert(false);
     }
-    catch (A*)
+    catch (A* p)
     {
+        assert(!p);
     }
     catch (const A* const)
     {
@@ -94,8 +98,9 @@
         throw nullptr;
         assert(false);
     }
-    catch (A const*)
+    catch (A const* p)
     {
+        assert(!p);
     }
     catch (A*)
     {
@@ -110,8 +115,9 @@
         throw nullptr;
         assert(false);
     }
-    catch (A*)
+    catch (A* p)
     {
+        assert(!p);
     }
     catch (A const*)
     {
Index: src/private_typeinfo.cpp
===================================================================
--- src/private_typeinfo.cpp
+++ src/private_typeinfo.cpp
@@ -171,8 +171,12 @@
 // catch (D2& d2) : adjustedPtr == &d2  (d2 is base class of thrown object)
 // catch (D2* d2) : adjustedPtr == d2
 // catch (D2*& d2) : adjustedPtr == d2
-// 
+//
 // catch (...) : adjustedPtr == & of the exception
+//
+// If the thrown type is nullptr_t and the caught type is a pointer to
+// member type, adjustedPtr points to a statically-allocated null pointer
+// representation of that type.
 
 // Handles bullet 1
 bool
@@ -337,12 +341,11 @@
     }
 }
 
-// Handles bullets 1 and 4 for both pointers and member pointers
+// Handles bullet 1 for both pointers and member pointers
 bool
 __pbase_type_info::can_catch(const __shim_type_info* thrown_type,
                              void*&) const
 {
-    if (is_equal(thrown_type, &typeid(std::nullptr_t), false)) return true;
     bool use_strcmp = this->__flags & (__incomplete_class_mask |
                                        __incomplete_mask);
     if (!use_strcmp) {
@@ -367,7 +370,13 @@
 __pointer_type_info::can_catch(const __shim_type_info* thrown_type,
                                void*& adjustedPtr) const
 {
-    // bullets 1 and 4
+    // bullet 4
+    if (is_equal(thrown_type, &typeid(std::nullptr_t), false)) {
+      adjustedPtr = nullptr;
+      return true;
+    }
+
+    // bullet 1
     if (__pbase_type_info::can_catch(thrown_type, adjustedPtr)) {
         if (adjustedPtr != NULL)
             adjustedPtr = *static_cast<void**>(adjustedPtr);
@@ -468,7 +477,22 @@
 
 bool __pointer_to_member_type_info::can_catch(
     const __shim_type_info* thrown_type, void*& adjustedPtr) const {
-    // bullets 1 and 4
+    // bullet 4
+    if (is_equal(thrown_type, &typeid(std::nullptr_t), false)) {
+      // We assume that the pointer to member representation is the same for
+      // all pointers to data members and for all pointers to member functions.
+      struct X {};
+      if (dynamic_cast<const __function_type_info*>(__pointee)) {
+        static int (X::*const null_ptr_rep)() = nullptr;
+        adjustedPtr = const_cast<int (X::**)()>(&null_ptr_rep);
+      } else {
+        static int X::*const null_ptr_rep = nullptr;
+        adjustedPtr = const_cast<int X::**>(&null_ptr_rep);
+      }
+      return true;
+    }
+
+    // bullet 1
     if (__pbase_type_info::can_catch(thrown_type, adjustedPtr))
         return true;
 
Index: src/CMakeLists.txt
===================================================================
--- src/CMakeLists.txt
+++ src/CMakeLists.txt
@@ -91,7 +91,7 @@
 
 set_target_properties(cxxabi_objects
   PROPERTIES
-    COMPILE_FLAGS "${LIBCXXABI_COMPILE_FLAGS}"
+    COMPILE_FLAGS "${LIBCXXABI_COMPILE_FLAGS} -fno-modules"
   )
 
 set(LIBCXXABI_TARGETS)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to