Author: rsmith Date: Tue Jul 19 15:19:37 2016 New Revision: 276016 URL: http://llvm.org/viewvc/llvm-project?rev=276016&view=rev Log: [libcxxabi] When catching an exception of type nullptr_t with a handler of pointer-to-member type, produce a null value of the right type.
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. Added: libcxxabi/trunk/test/catch_reference_nullptr.pass.cpp Modified: libcxxabi/trunk/src/private_typeinfo.cpp libcxxabi/trunk/test/catch_const_pointer_nullptr.pass.cpp libcxxabi/trunk/test/catch_member_pointer_nullptr.pass.cpp libcxxabi/trunk/test/catch_pointer_nullptr.pass.cpp libcxxabi/trunk/test/incomplete_type.sh.cpp Modified: libcxxabi/trunk/src/private_typeinfo.cpp URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/src/private_typeinfo.cpp?rev=276016&r1=276015&r2=276016&view=diff ============================================================================== --- libcxxabi/trunk/src/private_typeinfo.cpp (original) +++ libcxxabi/trunk/src/private_typeinfo.cpp Tue Jul 19 15:19:37 2016 @@ -171,8 +171,12 @@ __pointer_to_member_type_info::~__pointe // 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 @@ __vmi_class_type_info::has_unambiguous_p } } -// 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 @@ bool __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_type_info::can_catch_nest 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; Modified: libcxxabi/trunk/test/catch_const_pointer_nullptr.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/test/catch_const_pointer_nullptr.pass.cpp?rev=276016&r1=276015&r2=276016&view=diff ============================================================================== --- libcxxabi/trunk/test/catch_const_pointer_nullptr.pass.cpp (original) +++ libcxxabi/trunk/test/catch_const_pointer_nullptr.pass.cpp Tue Jul 19 15:19:37 2016 @@ -29,8 +29,9 @@ void test1() throw nullptr; assert(false); } - catch (A*) + catch (A* p) { + assert(!p); } catch (const A*) { @@ -46,8 +47,9 @@ void test2() throw nullptr; assert(false); } - catch (const A*) + catch (const A* p) { + assert(!p); } catch (A*) { @@ -62,8 +64,9 @@ void test3() throw nullptr; assert(false); } - catch (const A* const) + catch (const A* const p) { + assert(!p); } catch (A*) { @@ -78,8 +81,9 @@ void test4() throw nullptr; assert(false); } - catch (A*) + catch (A* p) { + assert(!p); } catch (const A* const) { @@ -94,8 +98,9 @@ void test5() throw nullptr; assert(false); } - catch (A const*) + catch (A const* p) { + assert(!p); } catch (A*) { @@ -110,8 +115,9 @@ void test6() throw nullptr; assert(false); } - catch (A*) + catch (A* p) { + assert(!p); } catch (A const*) { Modified: libcxxabi/trunk/test/catch_member_pointer_nullptr.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/test/catch_member_pointer_nullptr.pass.cpp?rev=276016&r1=276015&r2=276016&view=diff ============================================================================== --- libcxxabi/trunk/test/catch_member_pointer_nullptr.pass.cpp (original) +++ libcxxabi/trunk/test/catch_member_pointer_nullptr.pass.cpp Tue Jul 19 15:19:37 2016 @@ -29,8 +29,9 @@ void test1() throw nullptr; assert(false); } - catch (md2) + catch (md2 p) { + assert(!p); } catch (md1) { @@ -45,8 +46,9 @@ void test2() throw nullptr; assert(false); } - catch (md1) + catch (md1 p) { + assert(!p); } catch (md2) { Modified: libcxxabi/trunk/test/catch_pointer_nullptr.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/test/catch_pointer_nullptr.pass.cpp?rev=276016&r1=276015&r2=276016&view=diff ============================================================================== --- libcxxabi/trunk/test/catch_pointer_nullptr.pass.cpp (original) +++ libcxxabi/trunk/test/catch_pointer_nullptr.pass.cpp Tue Jul 19 15:19:37 2016 @@ -21,8 +21,9 @@ void test1() throw nullptr; assert(false); } - catch (int*) + catch (int* p) { + assert(!p); } catch (long*) { @@ -37,8 +38,9 @@ void test2() throw nullptr; assert(false); } - catch (A*) + catch (A* p) { + assert(!p); } catch (int*) { @@ -51,8 +53,8 @@ void catch_nullptr_test() { try { throw nullptr; assert(false); - } catch (Catch) { - // nothing todo + } catch (Catch c) { + assert(!c); } catch (...) { assert(false); } Added: libcxxabi/trunk/test/catch_reference_nullptr.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/test/catch_reference_nullptr.pass.cpp?rev=276016&view=auto ============================================================================== --- libcxxabi/trunk/test/catch_reference_nullptr.pass.cpp (added) +++ libcxxabi/trunk/test/catch_reference_nullptr.pass.cpp Tue Jul 19 15:19:37 2016 @@ -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 +} Modified: libcxxabi/trunk/test/incomplete_type.sh.cpp URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/test/incomplete_type.sh.cpp?rev=276016&r1=276015&r2=276016&view=diff ============================================================================== --- libcxxabi/trunk/test/incomplete_type.sh.cpp (original) +++ libcxxabi/trunk/test/incomplete_type.sh.cpp Tue Jul 19 15:19:37 2016 @@ -88,7 +88,9 @@ int main() { assert(false); } catch (int CompleteAtThrow::*) { assert(false); - } catch (int NeverDefined::*) {} + } catch (int NeverDefined::*p) { + assert(!p); + } AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompleteMP(), typeid(int IncompleteAtThrow::*)); try { ThrowIncompleteMP(); @@ -99,7 +101,9 @@ int main() { assert(false); } catch (IncompleteAtThrow**) { assert(false); - } catch (int IncompleteAtThrow::*) {} + } catch (int IncompleteAtThrow::*p) { + assert(!p); + } AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompletePP(), typeid(IncompleteAtThrow**)); try { @@ -107,7 +111,9 @@ int main() { assert(false); } catch (int IncompleteAtThrow::*) { assert(false); - } catch (IncompleteAtThrow**) {} + } catch (IncompleteAtThrow** p) { + assert(!p); + } try { ThrowIncompletePMP(); @@ -116,7 +122,9 @@ int main() { 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 @@ int main() { assert(false); } catch (CompleteAtThrow**) { assert(false); - } catch (int CompleteAtThrow::*) {} + } catch (int CompleteAtThrow::*p) { + assert(!p); + } AssertIncompleteTypeInfoEquals(ReturnTypeInfoCompletePP(), typeid(CompleteAtThrow**)); try { @@ -140,7 +150,9 @@ int main() { assert(false); } catch (int CompleteAtThrow::*) { assert(false); - } catch (CompleteAtThrow**) {} + } catch (CompleteAtThrow**p) { + assert(!p); + } try { ThrowCompletePMP(); @@ -153,22 +165,30 @@ int main() { 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits