rsmith created this revision. rsmith added a reviewer: EricWF. rsmith added a subscriber: cfe-commits. rsmith set the repository for this revision to rL LLVM.
This implements the following proposal from cxx-abi-dev: http://sourcerytools.com/pipermail/cxx-abi-dev/2016-October/002988.html ... which is necessary for complete support of http://wg21.link/p0012, specifically throwing noexcept function and member function pointers and catching them as non-noexcept pointers. Repository: rL LLVM https://reviews.llvm.org/D26178 Files: src/private_typeinfo.cpp src/private_typeinfo.h test/catch_function_03.pass.cpp test/catch_member_function_pointer_02.pass.cpp test/libcxxabi/test/config.py
Index: test/libcxxabi/test/config.py =================================================================== --- test/libcxxabi/test/config.py +++ test/libcxxabi/test/config.py @@ -37,6 +37,8 @@ super(Configuration, self).configure_features() if not self.get_lit_bool('enable_exceptions', True): self.config.available_features.add('libcxxabi-no-exceptions') + if not self.cxx.addCompileFlagIfSupported(['-Xclang', '-mqualified-function-type-info']): + self.config.available_features.add("libcxxabi-no-qualified-function-types") def configure_compile_flags(self): self.cxx.compile_flags += ['-DLIBCXXABI_NO_TIMER'] Index: test/catch_member_function_pointer_02.pass.cpp =================================================================== --- test/catch_member_function_pointer_02.pass.cpp +++ test/catch_member_function_pointer_02.pass.cpp @@ -0,0 +1,68 @@ +//===--------------- catch_member_function_pointer_02.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. +// +//===----------------------------------------------------------------------===// + +// Can a noexcept member function pointer be caught by a non-noexcept catch +// clause? +// UNSUPPORTED: c++98, c++03, c++11, c++14 +// UNSUPPORTED: libcxxabi-no-exceptions, libcxxabi-no-qualified-function-types + +#include <cassert> + +struct X { + template<bool Noexcept> void f() noexcept(Noexcept) {} +}; +template<bool Noexcept> using FnType = void (X::*)() noexcept(Noexcept); + +template<bool ThrowNoexcept, bool CatchNoexcept> +void check() +{ + try + { + auto p = &X::f<ThrowNoexcept>; + throw p; + assert(false); + } + catch (FnType<CatchNoexcept> p) + { + assert(ThrowNoexcept || !CatchNoexcept); + assert(p == &X::f<ThrowNoexcept>); + } + catch (...) + { + assert(!ThrowNoexcept && CatchNoexcept); + } +} + +void check_deep() { + FnType<true> p = &X::f<true>; + try + { + throw &p; + } + catch (FnType<false> *q) + { + assert(false); + } + catch (FnType<true> *q) + { + } + catch (...) + { + assert(false); + } +} + +int main() +{ + check<false, false>(); + check<false, true>(); + check<true, false>(); + check<true, true>(); + check_deep(); +} Index: test/catch_function_03.pass.cpp =================================================================== --- test/catch_function_03.pass.cpp +++ test/catch_function_03.pass.cpp @@ -0,0 +1,65 @@ +//===---------------------- catch_function_03.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. +// +//===----------------------------------------------------------------------===// + +// Can a noexcept function pointer be caught by a non-noexcept catch clause? +// UNSUPPORTED: c++98, c++03, c++11, c++14 +// UNSUPPORTED: libcxxabi-no-exceptions, libcxxabi-no-qualified-function-types + +#include <cassert> + +template<bool Noexcept> void f() noexcept(Noexcept) {} +template<bool Noexcept> using FnType = void() noexcept(Noexcept); + +template<bool ThrowNoexcept, bool CatchNoexcept> +void check() +{ + try + { + auto *p = f<ThrowNoexcept>; + throw p; + assert(false); + } + catch (FnType<CatchNoexcept> *p) + { + assert(ThrowNoexcept || !CatchNoexcept); + assert(p == &f<ThrowNoexcept>); + } + catch (...) + { + assert(!ThrowNoexcept && CatchNoexcept); + } +} + +void check_deep() { + auto *p = f<true>; + try + { + throw &p; + } + catch (FnType<false> **q) + { + assert(false); + } + catch (FnType<true> **q) + { + } + catch (...) + { + assert(false); + } +} + +int main() +{ + check<false, false>(); + check<false, true>(); + check<true, false>(); + check<true, true>(); + check_deep(); +} Index: src/private_typeinfo.h =================================================================== --- src/private_typeinfo.h +++ src/private_typeinfo.h @@ -49,6 +49,25 @@ void *&) const; }; +class _LIBCXXABI_TYPE_VIS __qualified_function_type_info : public __shim_type_info { +public: + const __function_type_info* __base_type; + unsigned int __qualifiers; + + enum __qualifiers_mask { + __const_mask = 0x01, + __volatile_mask = 0x02, + __restrict_mask = 0x04, + __lval_ref_mask = 0x08, + __rval_ref_mask = 0x10, + __noexcept_mask = 0x20, + __transaction_safe_mask = 0x40, + __noreturn_mask = 0x80 + }; + + _LIBCXXABI_HIDDEN virtual ~__qualified_function_type_info(); +}; + class _LIBCXXABI_TYPE_VIS __enum_type_info : public __shim_type_info { public: _LIBCXXABI_HIDDEN virtual ~__enum_type_info(); Index: src/private_typeinfo.cpp =================================================================== --- src/private_typeinfo.cpp +++ src/private_typeinfo.cpp @@ -105,6 +105,45 @@ { } +// __qualified_function_type_info + +__qualified_function_type_info::~__qualified_function_type_info() +{ +} + +// Determine if a function pointer conversion can convert a pointer (or pointer +// to member) to type x into a pointer (or pointer to member) to type y. +static bool is_function_pointer_conversion(const std::type_info* x, + const std::type_info* y) +{ + const unsigned int discardable_quals = + __qualified_function_type_info::__noexcept_mask | + __qualified_function_type_info::__transaction_safe_mask | + __qualified_function_type_info::__noreturn_mask; + + // If x has only discardable qualifiers and y is unqualified, then + // conversion is permitted. + const __qualified_function_type_info* qual_x = + dynamic_cast<const __qualified_function_type_info *>(x); + if (!qual_x) + return false; + if ((qual_x->__qualifiers & ~discardable_quals) == 0 && + is_equal(qual_x->__base_type, y, false)) + return true; + + // Otherwise, x's qualifiers must be the same as y's, plus some discardable + // ones. + const __qualified_function_type_info* qual_y = + dynamic_cast<const __qualified_function_type_info *>(y); + if (!qual_y) + return false; + if (qual_y->__qualifiers & ~qual_x->__qualifiers) + return false; + if (qual_x->__qualifiers & ~qual_y->__qualifiers & ~discardable_quals) + return false; + return is_equal(qual_x->__base_type, qual_y->__base_type, false); +} + // __enum_type_info __enum_type_info::~__enum_type_info() @@ -395,6 +434,10 @@ return false; if (is_equal(__pointee, thrown_pointer_type->__pointee, false)) return true; + // bullet 3C + if (is_function_pointer_conversion(thrown_pointer_type->__pointee, + __pointee)) + return true; // bullet 3A if (is_equal(__pointee, &typeid(void), false)) { // pointers to functions cannot be converted to void*. @@ -502,7 +545,9 @@ return false; if (thrown_pointer_type->__flags & ~__flags) return false; - if (!is_equal(__pointee, thrown_pointer_type->__pointee, false)) + if (!is_equal(__pointee, thrown_pointer_type->__pointee, false) && + !is_function_pointer_conversion(thrown_pointer_type->__pointee, + __pointee)) return false; if (is_equal(__context, thrown_pointer_type->__context, false)) return true;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits