Author: ericwf Date: Sun Sep 10 16:12:33 2017 New Revision: 312890 URL: http://llvm.org/viewvc/llvm-project?rev=312890&view=rev Log: Fix PR34298 - Allow std::function with an incomplete return type.
This patch fixes llvm.org/PR34298. Previously libc++ incorrectly evaluated the __invokable trait via the converting constructor `function(Tp)` [with Tp = std::function] whenever the copy constructor or copy assignment operator was required. This patch further constrains that constructor to short circut before evaluating the troublesome SFINAE when `Tp` matches std::function. The original patch is from Alex Lorenz. Modified: libcxx/trunk/include/functional libcxx/trunk/include/type_traits libcxx/trunk/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/F_incomplete.pass.cpp Modified: libcxx/trunk/include/functional URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/functional?rev=312890&r1=312889&r2=312890&view=diff ============================================================================== --- libcxx/trunk/include/functional (original) +++ libcxx/trunk/include/functional Sun Sep 10 16:12:33 2017 @@ -1597,9 +1597,11 @@ class _LIBCPP_TEMPLATE_VIS function<_Rp( return reinterpret_cast<__base*>(p); } - template <class _Fp, bool = !is_same<_Fp, function>::value && - __invokable<_Fp&, _ArgTypes...>::value> - struct __callable; + template <class _Fp, bool = __lazy_and< + integral_constant<bool, !is_same<__uncvref_t<_Fp>, function>::value>, + __invokable<_Fp&, _ArgTypes...> + >::value> + struct __callable; template <class _Fp> struct __callable<_Fp, true> { @@ -1612,6 +1614,9 @@ class _LIBCPP_TEMPLATE_VIS function<_Rp( { static const bool value = false; }; + + template <class _Fp> + using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type; public: typedef _Rp result_type; @@ -1622,9 +1627,7 @@ public: function(nullptr_t) _NOEXCEPT : __f_(0) {} function(const function&); function(function&&) _NOEXCEPT; - template<class _Fp, class = typename enable_if< - __callable<_Fp>::value && !is_same<_Fp, function>::value - >::type> + template<class _Fp, class = _EnableIfCallable<_Fp>> function(_Fp); #if _LIBCPP_STD_VER <= 14 @@ -1638,21 +1641,15 @@ public: function(allocator_arg_t, const _Alloc&, const function&); template<class _Alloc> function(allocator_arg_t, const _Alloc&, function&&); - template<class _Fp, class _Alloc, class = typename enable_if<__callable<_Fp>::value>::type> + template<class _Fp, class _Alloc, class = _EnableIfCallable<_Fp>> function(allocator_arg_t, const _Alloc& __a, _Fp __f); #endif function& operator=(const function&); function& operator=(function&&) _NOEXCEPT; function& operator=(nullptr_t) _NOEXCEPT; - template<class _Fp> - typename enable_if - < - __callable<typename decay<_Fp>::type>::value && - !is_same<typename remove_reference<_Fp>::type, function>::value, - function& - >::type - operator=(_Fp&&); + template<class _Fp, class = _EnableIfCallable<_Fp>> + function& operator=(_Fp&&); ~function(); @@ -1854,13 +1851,8 @@ function<_Rp(_ArgTypes...)>::operator=(n } template<class _Rp, class ..._ArgTypes> -template <class _Fp> -typename enable_if -< - function<_Rp(_ArgTypes...)>::template __callable<typename decay<_Fp>::type>::value && - !is_same<typename remove_reference<_Fp>::type, function<_Rp(_ArgTypes...)>>::value, - function<_Rp(_ArgTypes...)>& ->::type +template <class _Fp, class> +function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) { function(_VSTD::forward<_Fp>(__f)).swap(*this); Modified: libcxx/trunk/include/type_traits URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/type_traits?rev=312890&r1=312889&r2=312890&view=diff ============================================================================== --- libcxx/trunk/include/type_traits (original) +++ libcxx/trunk/include/type_traits Sun Sep 10 16:12:33 2017 @@ -4339,8 +4339,8 @@ struct __invokable_r using _Result = decltype( _VSTD::__invoke(_VSTD::declval<_Fp>(), _VSTD::declval<_Args>()...)); - static const bool value = - conditional< + using type = + typename conditional< !is_same<_Result, __nat>::value, typename conditional< is_void<_Ret>::value, @@ -4348,7 +4348,8 @@ struct __invokable_r is_convertible<_Result, _Ret> >::type, false_type - >::type::value; + >::type; + static const bool value = type::value; }; template <class _Fp, class ..._Args> Modified: libcxx/trunk/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/F_incomplete.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/F_incomplete.pass.cpp?rev=312890&r1=312889&r2=312890&view=diff ============================================================================== --- libcxx/trunk/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/F_incomplete.pass.cpp (original) +++ libcxx/trunk/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/F_incomplete.pass.cpp Sun Sep 10 16:12:33 2017 @@ -16,6 +16,7 @@ // Allow incomplete argument types in the __is_callable check #include <functional> +#include <cassert> struct X{ typedef std::function<void(X&)> callback_type; @@ -24,6 +25,40 @@ private: callback_type _cb; }; -int main() +struct IncompleteReturnType { + std::function<IncompleteReturnType ()> fn; +}; + + +int called = 0; +IncompleteReturnType test_fn() { + ++called; + IncompleteReturnType I; + return I; +} + +// See llvm.org/PR34298 +void test_pr34298() { + static_assert(std::is_copy_constructible<IncompleteReturnType>::value, ""); + static_assert(std::is_copy_assignable<IncompleteReturnType>::value, ""); + { + IncompleteReturnType X; + X.fn = test_fn; + const IncompleteReturnType& CX = X; + IncompleteReturnType X2 = CX; + assert(X2.fn); + assert(called == 0); + X2.fn(); + assert(called == 1); + } + { + const IncompleteReturnType Empty; + IncompleteReturnType X2 = Empty; + assert(!X2.fn); + } +} + +int main() { + test_pr34298(); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits