Commit 8c2d95c8 authored by Tom Stellard's avatar Tom Stellard
Browse files

Merging r312892:

------------------------------------------------------------------------
r312892 | ericwf | 2017-09-10 16:41:20 -0700 (Sun, 10 Sep 2017) | 10 lines

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.
------------------------------------------------------------------------

llvm-svn: 318835
parent 406ca546
Loading
Loading
Loading
Loading
+14 −22
Original line number Diff line number Diff line
@@ -1597,8 +1597,10 @@ class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
      return reinterpret_cast<__base*>(p);
    }

    template <class _Fp, bool = !is_same<_Fp, function>::value &&
                                __invokable<_Fp&, _ArgTypes...>::value>
    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(_ArgTypes...)>
        {
            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=(nullptr_t) _NOEXCEPT
}

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,
template <class _Fp, class>
function<_Rp(_ArgTypes...)>&
>::type
function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f)
{
    function(_VSTD::forward<_Fp>(__f)).swap(*this);
+4 −3
Original line number Diff line number Diff line
@@ -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>
+36 −1
Original line number Diff line number Diff line
@@ -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);
  }
  {
    IncompleteReturnType Empty;
    IncompleteReturnType X2 = Empty;
    assert(!X2.fn);
  }
}

int main() {
  test_pr34298();
}