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) 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(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(__pointee)) { + static int (X::*const null_ptr_rep)() = nullptr; + adjustedPtr = const_cast(&null_ptr_rep); + } else { + static int X::*const null_ptr_rep = nullptr; + adjustedPtr = const_cast(&null_ptr_rep); + } + return true; + } + + // bullet 1 if (__pbase_type_info::can_catch(thrown_type, adjustedPtr)) return true; 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: 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_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_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 +#include + +struct A {}; + +template +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(); + catch_nullptr_test(); + catch_nullptr_test(); + catch_nullptr_test(); + + // 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(); + catch_nullptr_test(); + catch_nullptr_test(); + catch_nullptr_test(); + catch_nullptr_test(); + catch_nullptr_test(); +#endif +} 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,7 +101,9 @@ assert(false); } catch (IncompleteAtThrow**) { assert(false); - } catch (int IncompleteAtThrow::*) {} + } catch (int IncompleteAtThrow::*p) { + assert(!p); + } AssertIncompleteTypeInfoEquals(ReturnTypeInfoIncompletePP(), typeid(IncompleteAtThrow**)); try { @@ -107,7 +111,9 @@ assert(false); } catch (int IncompleteAtThrow::*) { assert(false); - } catch (IncompleteAtThrow**) {} + } catch (IncompleteAtThrow** p) { + assert(!p); + } try { ThrowIncompletePMP(); @@ -116,7 +122,9 @@ 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